2022-09-26 15:12:13 -04:00
|
|
|
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
|
|
2023-04-05 11:20:44 -04:00
|
|
|
#include "Algo/Find.h"
|
2022-10-02 10:56:02 -04:00
|
|
|
#include "Async/TaskGraphInterfaces.h"
|
|
|
|
|
#include "Containers/Array.h"
|
|
|
|
|
#include "Containers/UnrealString.h"
|
|
|
|
|
#include "HAL/PlatformCrt.h"
|
|
|
|
|
#include "HAL/UnrealMemory.h"
|
|
|
|
|
#include "Logging/LogCategory.h"
|
|
|
|
|
#include "Logging/LogMacros.h"
|
|
|
|
|
#include "Math/IntPoint.h"
|
|
|
|
|
#include "Math/UnrealMathSSE.h"
|
|
|
|
|
#include "Misc/AssertionMacros.h"
|
2022-10-01 02:04:57 -04:00
|
|
|
#include "MuR/CodeRunner.h"
|
2023-02-22 04:22:07 -05:00
|
|
|
#include "MuR/System.h"
|
2023-04-05 11:20:44 -04:00
|
|
|
#include "MuR/ExtensionDataStreamer.h"
|
2022-10-02 10:56:02 -04:00
|
|
|
#include "MuR/Image.h"
|
2022-10-01 02:04:57 -04:00
|
|
|
#include "MuR/ImagePrivate.h"
|
2022-10-02 10:56:02 -04:00
|
|
|
#include "MuR/Layout.h"
|
|
|
|
|
#include "MuR/Mesh.h"
|
|
|
|
|
#include "MuR/Model.h"
|
|
|
|
|
#include "MuR/ModelPrivate.h"
|
|
|
|
|
#include "MuR/MutableMath.h"
|
|
|
|
|
#include "MuR/MutableTrace.h"
|
2022-10-01 02:04:57 -04:00
|
|
|
#include "MuR/OpImageBlend.h"
|
|
|
|
|
#include "MuR/OpImageNormalCombine.h"
|
2023-01-26 05:27:42 -05:00
|
|
|
#include "MuR/OpImageSaturate.h"
|
|
|
|
|
#include "MuR/OpImageInvert.h"
|
2022-10-02 10:56:02 -04:00
|
|
|
#include "MuR/Operations.h"
|
2022-10-01 02:04:57 -04:00
|
|
|
#include "MuR/Platform.h"
|
2022-10-02 10:56:02 -04:00
|
|
|
#include "MuR/Ptr.h"
|
|
|
|
|
#include "MuR/RefCounted.h"
|
|
|
|
|
#include "MuR/Serialisation.h"
|
|
|
|
|
#include "MuR/SerialisationPrivate.h"
|
|
|
|
|
#include "MuR/Settings.h"
|
|
|
|
|
#include "MuR/SystemPrivate.h"
|
2023-12-07 05:13:50 -05:00
|
|
|
#include "MuR/MutableRuntimeModule.h"
|
2022-10-02 10:56:02 -04:00
|
|
|
#include "Stats/Stats2.h"
|
|
|
|
|
#include "Templates/RefCounting.h"
|
|
|
|
|
#include "Templates/SharedPointer.h"
|
|
|
|
|
#include "Templates/Tuple.h"
|
|
|
|
|
#include "Trace/Detail/Channel.h"
|
2023-02-22 04:22:07 -05:00
|
|
|
#include "ProfilingDebugging/CountersTrace.h"
|
2023-08-31 14:28:27 -04:00
|
|
|
#include "Async/Fundamental/Scheduler.h"
|
2022-10-02 10:56:02 -04:00
|
|
|
|
2022-09-26 15:12:13 -04:00
|
|
|
|
|
|
|
|
DECLARE_CYCLE_STAT(TEXT("MutableCoreTask"), STAT_MutableCoreTask, STATGROUP_Game);
|
|
|
|
|
|
|
|
|
|
namespace mu
|
|
|
|
|
{
|
|
|
|
|
|
2023-02-22 04:22:07 -05:00
|
|
|
//---------------------------------------------------------------------------------------------
|
|
|
|
|
TRACE_DECLARE_INT_COUNTER(MutableRuntime_OpenTask, TEXT("MutableRuntime/OpenTask"));
|
|
|
|
|
TRACE_DECLARE_INT_COUNTER(MutableRuntime_ClosedTasks, TEXT("MutableRuntime/ClosedTasks"));
|
|
|
|
|
TRACE_DECLARE_INT_COUNTER(MutableRuntime_IssuedTasks, TEXT("MutableRuntime/IssuedTasks"));
|
2023-06-20 04:26:21 -04:00
|
|
|
TRACE_DECLARE_INT_COUNTER(MutableRuntime_IssuedTasksOnHold, TEXT("MutableRuntime/IssuedHoldTasks"));
|
2023-02-22 04:22:07 -05:00
|
|
|
|
|
|
|
|
void CodeRunner::UpdateTraces()
|
|
|
|
|
{
|
2023-06-20 04:26:21 -04:00
|
|
|
// Code Runner status
|
|
|
|
|
TRACE_COUNTER_SET(MutableRuntime_OpenTask, OpenTasks.Num());
|
|
|
|
|
TRACE_COUNTER_SET(MutableRuntime_ClosedTasks, ClosedTasks.Num());
|
|
|
|
|
TRACE_COUNTER_SET(MutableRuntime_IssuedTasks, IssuedTasks.Num());
|
|
|
|
|
TRACE_COUNTER_SET(MutableRuntime_IssuedTasksOnHold, IssuedTasksOnHold.Num());
|
2023-02-22 04:22:07 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2023-03-08 04:37:54 -05:00
|
|
|
//---------------------------------------------------------------------------------------------
|
|
|
|
|
/** This table encodes an heuristic for every execution stage of every operation that tries to
|
|
|
|
|
* guess the memory delta of that stage:
|
|
|
|
|
* - if negative: after the stage the working memory may have reduced
|
|
|
|
|
* - if positive: after the stage the working memory may have increased
|
|
|
|
|
* The actual number is just a unit-less weight that represents "amount of additional memory that will be used".
|
|
|
|
|
* Some operations don't have a constant memory delta and are dealt with specially, but still need
|
|
|
|
|
* to be in this table.
|
|
|
|
|
*/
|
|
|
|
|
static constexpr int32 sMemoryWeightsStageCount = 4;
|
|
|
|
|
static const int8 sMemoryWeights[int32(OP_TYPE::COUNT)][sMemoryWeightsStageCount] =
|
|
|
|
|
{
|
|
|
|
|
{ 0, 0, 0, 0 }, // NONE
|
|
|
|
|
|
|
|
|
|
{ 0, 0, 0, 0 }, // BO_CONSTANT
|
|
|
|
|
{ 0, 0, 0, 0 }, // NU_CONSTANT
|
|
|
|
|
{ 0, 0, 0, 0 }, // SC_CONSTANT
|
|
|
|
|
{ 0, 0, 0, 0 }, // CO_CONSTANT
|
|
|
|
|
{ 20, 0, 0, 0 }, // IM_CONSTANT
|
|
|
|
|
{ 20, 0, 0, 0 }, // ME_CONSTANT
|
|
|
|
|
{ 0, 0, 0, 0 }, // LA_CONSTANT
|
|
|
|
|
{ 0, 0, 0, 0 }, // PR_CONSTANT
|
|
|
|
|
{ 0, 0, 0, 0 }, // ST_CONSTANT
|
|
|
|
|
|
|
|
|
|
{ 0, 0, 0, 0 }, // BO_PARAMETER
|
|
|
|
|
{ 0, 0, 0, 0 }, // NU_PARAMETER
|
|
|
|
|
{ 0, 0, 0, 0 }, // SC_PARAMETER
|
|
|
|
|
{ 0, 0, 0, 0 }, // CO_PARAMETER
|
|
|
|
|
{ 0, 0, 0, 0 }, // PR_PARAMETER
|
|
|
|
|
{ 20, 0, 0, 0 }, // IM_PARAMETER
|
|
|
|
|
{ 0, 0, 0, 0 }, // ST_PARAMETER
|
|
|
|
|
|
2023-04-28 15:57:31 -04:00
|
|
|
{ 0, 0, 0, 0 }, // IM_REFERENCE
|
2024-05-14 07:28:21 -04:00
|
|
|
{ 0, 0, 0, 0 }, // ME_REFERENCE
|
2023-04-28 15:57:31 -04:00
|
|
|
|
2023-03-08 04:37:54 -05:00
|
|
|
{ 0, 0, 0, 0 }, // NU_CONDITIONAL
|
|
|
|
|
{ 0, 0, 0, 0 }, // SC_CONDITIONAL
|
|
|
|
|
{ 0, 0, 0, 0 }, // CO_CONDITIONAL
|
|
|
|
|
{ 0, 0, 0, 0 }, // IM_CONDITIONAL
|
|
|
|
|
{ 0, 0, 0, 0 }, // ME_CONDITIONAL
|
|
|
|
|
{ 0, 0, 0, 0 }, // LA_CONDITIONAL
|
|
|
|
|
{ 0, 0, 0, 0 }, // IN_CONDITIONAL
|
2023-08-03 12:40:31 -04:00
|
|
|
{ 0, 0, 0, 0 }, // ED_CONDITIONAL
|
2023-03-08 04:37:54 -05:00
|
|
|
|
|
|
|
|
{ 0, 0, 0, 0 }, // NU_SWITCH
|
|
|
|
|
{ 0, 0, 0, 0 }, // SC_SWITCH
|
|
|
|
|
{ 0, 0, 0, 0 }, // CO_SWITCH
|
|
|
|
|
{ 0, 0, 0, 0 }, // IM_SWITCH
|
|
|
|
|
{ 0, 0, 0, 0 }, // ME_SWITCH
|
|
|
|
|
{ 0, 0, 0, 0 }, // LA_SWITCH
|
|
|
|
|
{ 0, 0, 0, 0 }, // IN_SWITCH
|
2023-08-03 12:40:31 -04:00
|
|
|
{ 0, 0, 0, 0 }, // ED_SWITCH
|
2023-03-08 04:37:54 -05:00
|
|
|
|
|
|
|
|
{ 0, 0, 0, 0 }, // BO_LESS
|
|
|
|
|
{ 0, 0, 0, 0 }, // BO_EQUAL_SC_CONST
|
|
|
|
|
{ 0, 0, 0, 0 }, // BO_AND
|
|
|
|
|
{ 0, 0, 0, 0 }, // BO_OR
|
|
|
|
|
{ 0, 0, 0, 0 }, // BO_NOT
|
|
|
|
|
|
|
|
|
|
{ 0, 0, 0, 0 }, // SC_MULTIPLYADD
|
|
|
|
|
{ 0, 0, 0, 0 }, // SC_ARITHMETIC
|
|
|
|
|
{ 0, 0, 0, 0 }, // SC_CURVE
|
|
|
|
|
|
|
|
|
|
{ 0, 0, 0, 0 }, // CO_SAMPLEIMAGE
|
|
|
|
|
{ 0, 0, 0, 0 }, // CO_SWIZZLE
|
|
|
|
|
{ 0, 0, 0, 0 }, // CO_FROMSCALARS
|
|
|
|
|
{ 0, 0, 0, 0 }, // CO_ARITHMETIC
|
|
|
|
|
|
|
|
|
|
{ 0, -20, -20, 0 }, // IM_LAYER
|
|
|
|
|
{ 0, 0, 0, 0 }, // IM_LAYERCOLOUR
|
|
|
|
|
{ 0, 0, 0, 0 }, // IM_PIXELFORMAT (special case)
|
|
|
|
|
{ 0, 10, 0, 0 }, // IM_MIPMAP
|
|
|
|
|
{ 0, 0, 0, 0 }, // IM_RESIZE (special case)
|
|
|
|
|
{ 0, 0, 0, 0 }, // IM_RESIZELIKE (to be deprecated?)
|
|
|
|
|
{ 0, 0, 0, 0 }, // IM_RESIZEREL (special case)
|
|
|
|
|
{ 0, 20, 0, 0 }, // IM_BLANKLAYOUT
|
2023-10-09 04:49:01 -04:00
|
|
|
{ 0, -20, -20, 0 }, // IM_COMPOSE
|
2023-03-08 04:37:54 -05:00
|
|
|
{ 0, 0, -20, 0 }, // IM_INTERPOLATE
|
|
|
|
|
{ 0, 0, 0, 0 }, // IM_SATURATE
|
|
|
|
|
{ 0, 0, 0, 0 }, // IM_LUMINANCE
|
|
|
|
|
{ 0, 0, 0, 0 }, // IM_SWIZZLE
|
|
|
|
|
{ 0, 0, 0, 0 }, // IM_COLOURMAP
|
|
|
|
|
{ 0, 5, 0, 0 }, // IM_GRADIENT
|
|
|
|
|
{ 0, 0, 0, 0 }, // IM_BINARISE
|
|
|
|
|
{ 0, 20, 0, 0 }, // IM_PLAINCOLOUR
|
|
|
|
|
{ 0, -10, 0, 0 }, // IM_CROP
|
|
|
|
|
{ 0, -10, 0, 0 }, // IM_PATCH
|
|
|
|
|
{ 0, 10, 10, 0 }, // IM_RASTERMESH
|
|
|
|
|
{ 0, 0, 0, 0 }, // IM_MAKEGROWMAP
|
|
|
|
|
{ 0, -10, 0, 0 }, // IM_DISPLACE
|
|
|
|
|
{ 0, 0, -20, -20 }, // IM_MULTILAYER
|
|
|
|
|
{ 0, 0, 0, 0 }, // IM_INVERT
|
|
|
|
|
{ 0, 0, 0, 0 }, // IM_NORMALCOMPOSITE
|
|
|
|
|
{ 0, 0, 0, 0 }, // IM_TRANSFORM
|
|
|
|
|
|
|
|
|
|
{ 0, 0, 0, 0 }, // ME_APPLYLAYOUT
|
|
|
|
|
{ 0, -20, 0, 0 }, // ME_DIFFERENCE
|
|
|
|
|
{ 0, 0, -10, 0 }, // ME_MORPH2
|
|
|
|
|
{ 0, 0, 0, 0 }, // ME_MERGE
|
|
|
|
|
{ 0, 0, -20, 0 }, // ME_INTERPOLATE
|
|
|
|
|
{ 0, -10, 0, 0 }, // ME_MASKCLIPMESH
|
2023-10-24 06:20:03 -04:00
|
|
|
{ 0, -10, 0, 0 }, // ME_MASKCLIPUVMASK
|
2023-03-08 04:37:54 -05:00
|
|
|
{ 0, -10, 0, 0 }, // ME_MASKDIFF
|
|
|
|
|
{ 0, 0, -10, 0 }, // ME_REMOVEMASK
|
|
|
|
|
{ 0, 0, 0, 0 }, // ME_FORMAT
|
|
|
|
|
{ 0, -10, 0, 0 }, // ME_EXTRACTLAYOUTBLOCK
|
|
|
|
|
{ 0, 0, 0, 0 }, // ME_TRANSFORM
|
|
|
|
|
{ 0, -10, 0, 0 }, // ME_CLIPMORPHPLANE
|
|
|
|
|
{ 0, -10, 0, 0 }, // ME_CLIPWITHMESH
|
|
|
|
|
{ 0, 0, 0, 0 }, // ME_SETSKELETON
|
|
|
|
|
{ 0, 0, 0, 0 }, // ME_PROJECT
|
|
|
|
|
{ 0, 0, 0, 0 }, // ME_APPLYPOSE
|
|
|
|
|
{ 0, 0, 0, 0 }, // ME_GEOMETRYOPERATION
|
|
|
|
|
{ 0, -20, 0, 0 }, // ME_BINDSHAPE
|
|
|
|
|
{ 0, -10, 0, 0 }, // ME_APPLYSHAPE
|
|
|
|
|
{ 0, -10, 0, 0 }, // ME_CLIPDEFORM
|
|
|
|
|
{ 0, -10, 0, 0 }, // ME_MORPHRESHAPE
|
|
|
|
|
{ 0, 0, 0, 0 }, // ME_OPTIMIZESKINNING
|
2023-12-01 04:46:27 -05:00
|
|
|
{ 0, 0, 0, 0 }, // ME_ADDTAGS
|
|
|
|
|
|
2023-03-08 04:37:54 -05:00
|
|
|
{ 0, 0, 0, 0 }, // IN_ADDMESH
|
|
|
|
|
{ 0, 0, 0, 0 }, // IN_ADDIMAGE
|
|
|
|
|
{ 0, 0, 0, 0 }, // IN_ADDVECTOR
|
|
|
|
|
{ 0, 0, 0, 0 }, // IN_ADDSCALAR
|
|
|
|
|
{ 0, 0, 0, 0 }, // IN_ADDSTRING
|
|
|
|
|
{ 0, 0, 0, 0 }, // IN_ADDSURFACE
|
|
|
|
|
{ 0, 0, 0, 0 }, // IN_ADDCOMPONENT
|
2023-09-26 04:43:37 -04:00
|
|
|
{ 0, 0, 0, 0 }, // IN_ADDLOD
|
|
|
|
|
{ 0, 0, 0, 0 }, // IN_ADDEXTENSIONDATA
|
|
|
|
|
|
2023-03-08 04:37:54 -05:00
|
|
|
{ 0, 0, 0, 0 }, // LA_PACK
|
|
|
|
|
{ 0, 0, 0, 0 }, // LA_MERGE
|
|
|
|
|
{ 0, -1, 0, 0 }, // LA_REMOVEBLOCKS
|
|
|
|
|
{ 0, 0, 0, 0 }, // LA_FROMMESH
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static_assert(sizeof(sMemoryWeights)/sMemoryWeightsStageCount == int32(OP_TYPE::COUNT));
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int32 CodeRunner::GetOpEstimatedMemoryDelta( const FScheduledOp& Candidate, const FProgram& Program )
|
|
|
|
|
{
|
|
|
|
|
int32 OpDelta = 0;
|
|
|
|
|
int32 Stage = FMath::Min(int32(Candidate.Stage), sMemoryWeightsStageCount - 1);
|
|
|
|
|
OP_TYPE OpType = Program.GetOpType(Candidate.At);
|
|
|
|
|
|
|
|
|
|
if (OpType == OP_TYPE::IM_PIXELFORMAT && Stage == 1)
|
|
|
|
|
{
|
|
|
|
|
// TODO: We should get the actual format from the arguments for a more precise calculation.
|
|
|
|
|
OP::ImagePixelFormatArgs args = Program.GetOpArgs<OP::ImagePixelFormatArgs>(Candidate.At);
|
|
|
|
|
bool bIsCompressed = GetUncompressedFormat(args.format) != args.format;
|
2023-06-23 02:01:13 -04:00
|
|
|
bool bIsSmallish = GetImageFormatData(args.format).BytesPerBlock < 2;
|
2023-03-08 04:37:54 -05:00
|
|
|
if (bIsCompressed || bIsSmallish)
|
|
|
|
|
{
|
|
|
|
|
OpDelta = -10;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
OpDelta = 10;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
OpDelta = sMemoryWeights[int32(OpType)][Stage];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return OpDelta;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2023-06-20 04:26:21 -04:00
|
|
|
//---------------------------------------------------------------------------------------------
|
|
|
|
|
void CodeRunner::LaunchIssuedTask( const TSharedPtr<FIssuedTask>& TaskToIssue, bool& bOutFailed )
|
|
|
|
|
{
|
|
|
|
|
bool bFailed = false;
|
|
|
|
|
bool bHasWork = TaskToIssue->Prepare(this, bFailed);
|
|
|
|
|
if (bFailed)
|
|
|
|
|
{
|
|
|
|
|
bUnrecoverableError = true;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Launch it
|
|
|
|
|
if (bHasWork)
|
|
|
|
|
{
|
|
|
|
|
if (bForceSerialTaskExecution)
|
|
|
|
|
{
|
|
|
|
|
TaskToIssue->Event = {};
|
|
|
|
|
TaskToIssue->DoWork();
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
TaskToIssue->Event = UE::Tasks::Launch(TEXT("MutableCore_Task"),
|
|
|
|
|
[TaskToIssue]() { TaskToIssue->DoWork(); },
|
|
|
|
|
UE::Tasks::ETaskPriority::Inherit);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Remember it for later processing.
|
|
|
|
|
IssuedTasks.Add(TaskToIssue);
|
|
|
|
|
}
|
|
|
|
|
|
2024-03-13 07:52:36 -04:00
|
|
|
UE::Tasks::FTask CodeRunner::StartRun(bool bForceInlineExecution)
|
|
|
|
|
{
|
|
|
|
|
check(RunnerCompletionEvent.IsCompleted());
|
2023-06-20 04:26:21 -04:00
|
|
|
|
2022-09-26 15:12:13 -04:00
|
|
|
bUnrecoverableError = false;
|
|
|
|
|
|
2024-01-23 09:51:10 -05:00
|
|
|
m_heapData.SetNum(0, EAllowShrinking::No);
|
|
|
|
|
m_heapImageDesc.SetNum(0, EAllowShrinking::No);
|
2024-03-13 07:52:36 -04:00
|
|
|
|
|
|
|
|
RunnerCompletionEvent = UE::Tasks::FTaskEvent(TEXT("CodeRunnerCompletionEvent"));
|
|
|
|
|
|
2022-09-26 15:12:13 -04:00
|
|
|
bool bProfile = false;
|
2024-03-13 07:52:36 -04:00
|
|
|
|
|
|
|
|
TUniquePtr<FProfileContext> ProfileContext = bProfile ? MakeUnique<FProfileContext>() : nullptr;
|
|
|
|
|
Run(MoveTemp(ProfileContext), bForceInlineExecution);
|
2022-09-26 15:12:13 -04:00
|
|
|
|
2024-03-13 07:52:36 -04:00
|
|
|
check(!bForceInlineExecution || RunnerCompletionEvent.IsCompleted());
|
|
|
|
|
|
|
|
|
|
return RunnerCompletionEvent;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CodeRunner::AbortRun()
|
|
|
|
|
{
|
|
|
|
|
bUnrecoverableError = true;
|
|
|
|
|
RunnerCompletionEvent.Trigger();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CodeRunner::Run(TUniquePtr<FProfileContext>&& ProfileContext, bool bForceInlineExecution)
|
|
|
|
|
{
|
2024-03-19 06:36:12 -04:00
|
|
|
MUTABLE_CPUPROFILER_SCOPE(CodeRunner_Run);
|
2024-03-13 07:52:36 -04:00
|
|
|
|
|
|
|
|
check(!RunnerCompletionEvent.IsCompleted());
|
|
|
|
|
|
2024-03-19 06:36:12 -04:00
|
|
|
// TODO: Move MaxAllowedTime somewhere else more accessible, maybe a cvar.
|
|
|
|
|
const FTimespan MaxAllowedTime = FTimespan::FromMilliseconds(2.0);
|
|
|
|
|
const FTimespan TimeOut = FTimespan::FromSeconds(FPlatformTime::Seconds()) + MaxAllowedTime;
|
2024-03-13 07:52:36 -04:00
|
|
|
|
2024-06-14 04:01:28 -04:00
|
|
|
bool bSuccess = true;
|
|
|
|
|
|
2024-03-13 07:52:36 -04:00
|
|
|
while(!OpenTasks.IsEmpty() || !ClosedTasks.IsEmpty() || !IssuedTasks.IsEmpty())
|
2022-09-26 15:12:13 -04:00
|
|
|
{
|
2023-02-22 04:22:07 -05:00
|
|
|
UpdateTraces();
|
2022-09-26 15:12:13 -04:00
|
|
|
// Debug: log the amount of tasks that we'd be able to run concurrently:
|
|
|
|
|
//{
|
|
|
|
|
// int32 ClosedReady = ClosedTasks.Num();
|
|
|
|
|
// for (int Index = ClosedTasks.Num() - 1; Index >= 0; --Index)
|
|
|
|
|
// {
|
2022-11-23 03:17:56 -05:00
|
|
|
// for (const FCacheAddress& Dep : ClosedTasks[Index].Deps)
|
2022-09-26 15:12:13 -04:00
|
|
|
// {
|
|
|
|
|
// if (Dep.at && !GetMemory().IsValid(Dep))
|
|
|
|
|
// {
|
|
|
|
|
// --ClosedReady;
|
|
|
|
|
// continue;
|
|
|
|
|
// }
|
|
|
|
|
// }
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
// UE_LOG(LogMutableCore, Log, TEXT("Tasks: %5d open, %5d issued, %5d closed, %d closed ready"), OpenTasks.Num(), IssuedTasks.Num(), ClosedTasks.Num(), ClosedReady);
|
|
|
|
|
//}
|
|
|
|
|
|
2024-06-14 04:01:28 -04:00
|
|
|
for (int32 Index = 0; bSuccess && Index < IssuedTasks.Num(); )
|
2022-09-26 15:12:13 -04:00
|
|
|
{
|
|
|
|
|
check(IssuedTasks[Index]);
|
|
|
|
|
|
2024-03-13 07:52:36 -04:00
|
|
|
bool bWorkDone = IssuedTasks[Index]->IsComplete(this);
|
|
|
|
|
if (bWorkDone)
|
2022-09-26 15:12:13 -04:00
|
|
|
{
|
2022-11-23 03:17:56 -05:00
|
|
|
const FScheduledOp& item = IssuedTasks[Index]->Op;
|
2024-06-14 04:01:28 -04:00
|
|
|
bSuccess = IssuedTasks[Index]->Complete(this);
|
2022-09-26 15:12:13 -04:00
|
|
|
|
2022-11-23 03:17:56 -05:00
|
|
|
if (ScheduledStagePerOp[item] == item.Stage + 1)
|
2022-09-26 15:12:13 -04:00
|
|
|
{
|
|
|
|
|
// We completed everything that was requested, clear it otherwise if needed
|
|
|
|
|
// again it is not going to be rebuilt.
|
|
|
|
|
// \TODO: track rebuilds.
|
|
|
|
|
ScheduledStagePerOp[item] = 0;
|
|
|
|
|
}
|
|
|
|
|
|
2024-03-13 07:52:36 -04:00
|
|
|
IssuedTasks.RemoveAt(Index, EAllowShrinking::No); // with swap? changes order of execution.
|
2022-09-26 15:12:13 -04:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
++Index;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-14 04:01:28 -04:00
|
|
|
if (!bSuccess)
|
|
|
|
|
{
|
|
|
|
|
return AbortRun();
|
|
|
|
|
}
|
|
|
|
|
|
2022-09-26 15:12:13 -04:00
|
|
|
while (!OpenTasks.IsEmpty())
|
|
|
|
|
{
|
2023-03-08 04:37:54 -05:00
|
|
|
// Get a new task to run
|
|
|
|
|
FScheduledOp item;
|
|
|
|
|
switch (ExecutionStrategy)
|
|
|
|
|
{
|
2023-06-20 04:26:21 -04:00
|
|
|
//case EExecutionStrategy::MinimizeMemory:
|
|
|
|
|
//{
|
|
|
|
|
// // TODO: This should be done when an operation is added to the OpenTasks array instead of every time.
|
|
|
|
|
// int32 BestOp = 0;
|
|
|
|
|
// int8 BestDelta = TNumericLimits<int8>::Max();
|
|
|
|
|
// int32 OpenOpCount = OpenTasks.Num();
|
|
|
|
|
// const FProgram& Program = m_pModel->GetPrivate()->m_program;
|
|
|
|
|
// for (int32 OpIndex = 0; OpIndex < OpenOpCount; ++OpIndex)
|
|
|
|
|
// {
|
|
|
|
|
// const FScheduledOp& Candidate = OpenTasks[OpIndex];
|
|
|
|
|
// int32 OpDelta = GetOpEstimatedMemoryDelta(Candidate, Program);
|
2023-03-08 04:37:54 -05:00
|
|
|
|
2023-06-20 04:26:21 -04:00
|
|
|
// if (OpDelta < BestDelta)
|
|
|
|
|
// {
|
|
|
|
|
// BestOp = OpIndex;
|
|
|
|
|
// BestDelta = OpDelta;
|
2023-03-08 04:37:54 -05:00
|
|
|
|
2023-06-20 04:26:21 -04:00
|
|
|
// // Shortcut: If we are freeing memory, we don't care how much: it is already the best op
|
|
|
|
|
// if (BestDelta < 0)
|
|
|
|
|
// {
|
|
|
|
|
// break;
|
|
|
|
|
// }
|
|
|
|
|
// }
|
|
|
|
|
// }
|
2023-03-08 04:37:54 -05:00
|
|
|
|
2023-06-20 04:26:21 -04:00
|
|
|
// item = OpenTasks[BestOp];
|
2024-02-19 16:51:58 -05:00
|
|
|
// OpenTasks.RemoveAtSwap(BestOp,EAllowShrinking::No);
|
2023-06-20 04:26:21 -04:00
|
|
|
// break;
|
|
|
|
|
//}
|
2023-03-08 04:37:54 -05:00
|
|
|
|
|
|
|
|
case EExecutionStrategy::None:
|
|
|
|
|
default:
|
|
|
|
|
// Just get one.
|
2024-01-23 09:51:10 -05:00
|
|
|
item = OpenTasks.Pop(EAllowShrinking::No);
|
2023-03-08 04:37:54 -05:00
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
}
|
2022-09-26 15:12:13 -04:00
|
|
|
|
|
|
|
|
// Special processing in case it is an ImageDesc operation
|
2024-03-13 07:52:36 -04:00
|
|
|
if (item.Type == FScheduledOp::EType::ImageDesc)
|
2022-09-26 15:12:13 -04:00
|
|
|
{
|
2022-12-09 03:57:57 -05:00
|
|
|
RunCodeImageDesc(item, m_pParams, m_pModel.Get(), m_lodMask);
|
2022-09-26 15:12:13 -04:00
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Don't run it if we already have the result.
|
2022-11-23 03:17:56 -05:00
|
|
|
FCacheAddress cat(item);
|
2022-09-26 15:12:13 -04:00
|
|
|
if (GetMemory().IsValid(cat))
|
|
|
|
|
{
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// See if we can schedule this item concurrently
|
|
|
|
|
TSharedPtr<FIssuedTask> IssuedTask = IssueOp(item);
|
|
|
|
|
if (IssuedTask)
|
|
|
|
|
{
|
2023-06-20 04:26:21 -04:00
|
|
|
if (ShouldIssueTask())
|
2022-09-26 15:12:13 -04:00
|
|
|
{
|
2023-06-20 04:26:21 -04:00
|
|
|
bool bFailed = false;
|
|
|
|
|
LaunchIssuedTask(IssuedTask, bFailed);
|
|
|
|
|
if (bFailed)
|
2022-09-26 15:12:13 -04:00
|
|
|
{
|
2024-03-13 07:52:36 -04:00
|
|
|
return AbortRun();
|
2022-09-26 15:12:13 -04:00
|
|
|
}
|
|
|
|
|
}
|
2023-06-20 04:26:21 -04:00
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
IssuedTasksOnHold.Add(IssuedTask);
|
|
|
|
|
}
|
2022-09-26 15:12:13 -04:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
// Run immediately
|
2024-08-13 10:23:49 -04:00
|
|
|
RunCode(item, m_pParams, m_pModel, m_lodMask);
|
2022-09-26 15:12:13 -04:00
|
|
|
|
2022-11-23 03:17:56 -05:00
|
|
|
if (ScheduledStagePerOp[item] == item.Stage + 1)
|
2022-09-26 15:12:13 -04:00
|
|
|
{
|
2023-06-20 04:26:21 -04:00
|
|
|
// We completed everything that was requested, clear it. Otherwise if needed again it is not going to be rebuilt.
|
|
|
|
|
// \TODO: track operations that are run more than once?.
|
2022-09-26 15:12:13 -04:00
|
|
|
ScheduledStagePerOp[item] = 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-03-13 07:52:36 -04:00
|
|
|
if (ProfileContext)
|
2022-09-26 15:12:13 -04:00
|
|
|
{
|
2024-03-13 07:52:36 -04:00
|
|
|
++ProfileContext->NumRunOps;
|
|
|
|
|
++ProfileContext->RunOpsPerType[int32(m_pModel->GetPrivate()->m_program.GetOpType(item.At))];
|
2022-09-26 15:12:13 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-06-20 04:26:21 -04:00
|
|
|
UpdateTraces();
|
|
|
|
|
|
|
|
|
|
// Look for tasks on hold and see if we can launch them
|
|
|
|
|
while (IssuedTasksOnHold.Num() && ShouldIssueTask())
|
|
|
|
|
{
|
2024-01-23 09:51:10 -05:00
|
|
|
TSharedPtr<FIssuedTask> TaskToIssue = IssuedTasksOnHold.Pop(EAllowShrinking::No);
|
2023-06-20 04:26:21 -04:00
|
|
|
|
|
|
|
|
bool bFailed = false;
|
|
|
|
|
LaunchIssuedTask(TaskToIssue, bFailed);
|
|
|
|
|
if (bFailed)
|
|
|
|
|
{
|
2024-03-13 07:52:36 -04:00
|
|
|
return AbortRun();
|
2023-06-20 04:26:21 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-03-08 04:37:54 -05:00
|
|
|
// Look for a closed task with dependencies satisfied and move them to the open task list.
|
2022-09-26 15:12:13 -04:00
|
|
|
bool bSomeWasReady = false;
|
2024-03-13 07:52:36 -04:00
|
|
|
for (int32 Index = 0; Index < ClosedTasks.Num(); )
|
2022-09-26 15:12:13 -04:00
|
|
|
{
|
|
|
|
|
bool Ready = true;
|
2022-11-23 03:17:56 -05:00
|
|
|
for ( const FCacheAddress& Dep: ClosedTasks[Index].Deps )
|
2022-09-26 15:12:13 -04:00
|
|
|
{
|
2023-05-24 02:50:03 -04:00
|
|
|
bool bDependencyFailed = false;
|
|
|
|
|
bDependencyFailed = Dep.At && !GetMemory().IsValid(Dep);
|
2022-09-26 15:12:13 -04:00
|
|
|
|
2023-05-24 02:50:03 -04:00
|
|
|
if (bDependencyFailed)
|
2022-09-26 15:12:13 -04:00
|
|
|
{
|
|
|
|
|
Ready = false;
|
2023-05-24 02:50:03 -04:00
|
|
|
break;
|
2022-09-26 15:12:13 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (Ready)
|
|
|
|
|
{
|
|
|
|
|
bSomeWasReady = true;
|
|
|
|
|
FTask Task = ClosedTasks[Index];
|
2024-02-19 16:51:58 -05:00
|
|
|
ClosedTasks.RemoveAt(Index, EAllowShrinking::No); // with swap? would change order of execution.
|
2022-09-26 15:12:13 -04:00
|
|
|
OpenTasks.Push(Task.Op);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
++Index;
|
|
|
|
|
}
|
2023-02-22 04:22:07 -05:00
|
|
|
|
2022-09-26 15:12:13 -04:00
|
|
|
}
|
|
|
|
|
|
2023-06-20 04:26:21 -04:00
|
|
|
UpdateTraces();
|
|
|
|
|
|
2022-09-26 15:12:13 -04:00
|
|
|
// Debug: Did we dead-lock?
|
|
|
|
|
bool bDeadLock = !(OpenTasks.Num() || IssuedTasks.Num() || !ClosedTasks.Num() || bSomeWasReady);
|
|
|
|
|
if (bDeadLock)
|
|
|
|
|
{
|
|
|
|
|
// Log the task graph
|
2024-03-13 07:52:36 -04:00
|
|
|
for (int32 Index = 0; Index < ClosedTasks.Num(); ++Index)
|
2022-09-26 15:12:13 -04:00
|
|
|
{
|
2022-11-23 03:17:56 -05:00
|
|
|
FString TaskDesc = FString::Printf(TEXT("Closed task %d-%d-%d depends on : "), ClosedTasks[Index].Op.At, ClosedTasks[Index].Op.ExecutionIndex, ClosedTasks[Index].Op.Stage );
|
|
|
|
|
for (const FCacheAddress& Dep : ClosedTasks[Index].Deps)
|
2022-09-26 15:12:13 -04:00
|
|
|
{
|
2022-11-23 03:17:56 -05:00
|
|
|
if (Dep.At && !GetMemory().IsValid(Dep))
|
2022-09-26 15:12:13 -04:00
|
|
|
{
|
2022-11-23 03:17:56 -05:00
|
|
|
TaskDesc += FString::Printf(TEXT("%d-%d, "), Dep.At, Dep.ExecutionIndex);
|
2022-09-26 15:12:13 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
UE_LOG(LogMutableCore, Log, TEXT("%s"), *TaskDesc);
|
|
|
|
|
}
|
|
|
|
|
check(false);
|
2023-03-08 04:37:54 -05:00
|
|
|
|
|
|
|
|
// This should never happen but if it does, abort the code execution.
|
2024-03-13 07:52:36 -04:00
|
|
|
return AbortRun();
|
2022-09-26 15:12:13 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// If at this point there is no open op and we haven't finished, we need to wait for an issued op to complete.
|
|
|
|
|
if (OpenTasks.IsEmpty() && !IssuedTasks.IsEmpty())
|
|
|
|
|
{
|
2024-03-19 06:36:12 -04:00
|
|
|
if (!bForceInlineExecution)
|
2022-09-26 15:12:13 -04:00
|
|
|
{
|
2024-03-19 06:36:12 -04:00
|
|
|
TArray<UE::Tasks::FTask, TInlineAllocator<8>> IssuedTasksCompletionEvents;
|
|
|
|
|
IssuedTasksCompletionEvents.Reserve(IssuedTasks.Num());
|
|
|
|
|
|
|
|
|
|
for (TSharedPtr<FIssuedTask>& IssuedTask : IssuedTasks)
|
2022-09-26 15:12:13 -04:00
|
|
|
{
|
2024-03-19 06:36:12 -04:00
|
|
|
if (IssuedTask->Event.IsValid())
|
2023-08-31 14:28:27 -04:00
|
|
|
{
|
2024-03-19 06:36:12 -04:00
|
|
|
IssuedTasksCompletionEvents.Add(IssuedTask->Event);
|
2023-08-31 14:28:27 -04:00
|
|
|
}
|
2024-03-19 06:36:12 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
m_pSystem->WorkingMemoryManager.InvalidateRunnerThread();
|
|
|
|
|
|
|
|
|
|
UE::Tasks::Launch(TEXT("CodeRunnerFromIssuedTasksTask"),
|
|
|
|
|
[Runner = AsShared(), ProfileContext = MoveTemp(ProfileContext)]() mutable
|
|
|
|
|
{
|
|
|
|
|
Runner->m_pSystem->WorkingMemoryManager.ResetRunnerThread();
|
|
|
|
|
|
|
|
|
|
constexpr bool bForceInlineExecution = false;
|
|
|
|
|
Runner->Run(MoveTemp(ProfileContext), bForceInlineExecution);
|
|
|
|
|
},
|
|
|
|
|
UE::Tasks::Prerequisites(UE::Tasks::Any(IssuedTasksCompletionEvents)),
|
|
|
|
|
UE::Tasks::ETaskPriority::Inherit);
|
|
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
MUTABLE_CPUPROFILER_SCOPE(CodeRunner_WaitIssued);
|
|
|
|
|
for (int32 IssuedIndex = 0; IssuedIndex < IssuedTasks.Num(); ++IssuedIndex)
|
|
|
|
|
{
|
|
|
|
|
if (IssuedTasks[IssuedIndex]->Event.IsValid())
|
2023-08-31 14:28:27 -04:00
|
|
|
{
|
2024-06-12 13:56:22 -04:00
|
|
|
IssuedTasks[IssuedIndex]->Event.Wait();
|
2024-03-01 05:39:11 -05:00
|
|
|
|
2024-03-13 07:52:36 -04:00
|
|
|
break;
|
|
|
|
|
}
|
2022-09-26 15:12:13 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-03-13 07:52:36 -04:00
|
|
|
|
|
|
|
|
if (!bForceInlineExecution)
|
|
|
|
|
{
|
2024-03-19 06:36:12 -04:00
|
|
|
if (FTimespan::FromSeconds(FPlatformTime::Seconds()) > TimeOut)
|
2024-03-13 07:52:36 -04:00
|
|
|
{
|
|
|
|
|
m_pSystem->WorkingMemoryManager.InvalidateRunnerThread();
|
|
|
|
|
|
|
|
|
|
UE::Tasks::Launch(TEXT("CodeRunnerFromTimeoutTask"),
|
|
|
|
|
[Runner = AsShared(), ProfileContext = MoveTemp(ProfileContext)]() mutable
|
|
|
|
|
{
|
|
|
|
|
Runner->m_pSystem->WorkingMemoryManager.ResetRunnerThread();
|
|
|
|
|
|
|
|
|
|
constexpr bool bForceInlineExecution = false;
|
|
|
|
|
Runner->Run(MoveTemp(ProfileContext), bForceInlineExecution);
|
|
|
|
|
},
|
|
|
|
|
UE::Tasks::ETaskPriority::Inherit);
|
|
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-09-26 15:12:13 -04:00
|
|
|
}
|
|
|
|
|
|
2024-03-13 07:52:36 -04:00
|
|
|
if (ProfileContext)
|
2022-09-26 15:12:13 -04:00
|
|
|
{
|
|
|
|
|
UE_LOG(LogMutableCore, Log, TEXT("Mutable Heap Bytes: %d"), m_heapData.Num()* m_heapData.GetTypeSize());
|
2024-03-13 07:52:36 -04:00
|
|
|
UE_LOG(LogMutableCore, Log, TEXT("Ran ops : %5d "), ProfileContext->NumRunOps);
|
2022-09-26 15:12:13 -04:00
|
|
|
|
2024-03-13 07:52:36 -04:00
|
|
|
constexpr int32 HistogramSize = 8;
|
2022-09-26 15:12:13 -04:00
|
|
|
int32 MostCommonOps[HistogramSize] = {};
|
2024-03-13 07:52:36 -04:00
|
|
|
for (int32 OpIndex = 0; OpIndex < int32(OP_TYPE::COUNT); ++OpIndex)
|
2022-09-26 15:12:13 -04:00
|
|
|
{
|
2024-03-13 07:52:36 -04:00
|
|
|
for (int32 HistIndex = 0; HistIndex < HistogramSize; ++HistIndex)
|
2022-09-26 15:12:13 -04:00
|
|
|
{
|
2024-03-13 07:52:36 -04:00
|
|
|
if (ProfileContext->RunOpsPerType[OpIndex] > ProfileContext->RunOpsPerType[MostCommonOps[HistIndex]])
|
2022-09-26 15:12:13 -04:00
|
|
|
{
|
|
|
|
|
// Displace others
|
2024-03-13 07:52:36 -04:00
|
|
|
int32 ElementsToMove = HistogramSize - HistIndex - 1;
|
2022-09-26 15:12:13 -04:00
|
|
|
if (ElementsToMove > 0)
|
|
|
|
|
{
|
2024-03-13 07:52:36 -04:00
|
|
|
FMemory::Memcpy(&MostCommonOps[HistIndex + 1], &MostCommonOps[HistIndex], sizeof(int32)*ElementsToMove);
|
2022-09-26 15:12:13 -04:00
|
|
|
}
|
|
|
|
|
// Set new value
|
2024-03-13 07:52:36 -04:00
|
|
|
MostCommonOps[HistIndex] = OpIndex;
|
2022-09-26 15:12:13 -04:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-03-13 07:52:36 -04:00
|
|
|
for (int32 HistIndex = 0; HistIndex < HistogramSize; ++HistIndex)
|
2022-09-26 15:12:13 -04:00
|
|
|
{
|
2024-03-13 07:52:36 -04:00
|
|
|
UE_LOG(LogMutableCore, Log, TEXT(" op %4d, %4d times."), MostCommonOps[HistIndex], ProfileContext->RunOpsPerType[MostCommonOps[HistIndex]]);
|
2022-09-26 15:12:13 -04:00
|
|
|
}
|
|
|
|
|
}
|
2024-03-13 07:52:36 -04:00
|
|
|
|
|
|
|
|
RunnerCompletionEvent.Trigger();
|
2022-09-26 15:12:13 -04:00
|
|
|
}
|
|
|
|
|
|
2023-07-24 06:17:34 -04:00
|
|
|
|
2022-09-26 15:12:13 -04:00
|
|
|
//---------------------------------------------------------------------------------------------
|
|
|
|
|
void CodeRunner::GetImageDescResult(FImageDesc& OutDesc)
|
|
|
|
|
{
|
|
|
|
|
check( m_heapImageDesc.Num()>0 );
|
|
|
|
|
OutDesc = m_heapImageDesc[0];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//---------------------------------------------------------------------------------------------
|
|
|
|
|
//---------------------------------------------------------------------------------------------
|
|
|
|
|
//---------------------------------------------------------------------------------------------
|
|
|
|
|
class FImageLayerTask : public CodeRunner::FIssuedTask
|
|
|
|
|
{
|
|
|
|
|
public:
|
2023-05-24 02:50:03 -04:00
|
|
|
FImageLayerTask(const FScheduledOp& InOp, const OP::ImageLayerArgs& InArgs)
|
|
|
|
|
: FIssuedTask(InOp), Args(InArgs)
|
|
|
|
|
{}
|
2022-09-26 15:12:13 -04:00
|
|
|
|
|
|
|
|
// FIssuedTask interface
|
2024-06-14 04:01:28 -04:00
|
|
|
virtual bool Prepare(CodeRunner*, bool& bOutFailed) override;
|
|
|
|
|
virtual void DoWork() override;
|
|
|
|
|
virtual bool Complete(CodeRunner*) override;
|
2022-09-26 15:12:13 -04:00
|
|
|
|
|
|
|
|
private:
|
2023-05-24 02:50:03 -04:00
|
|
|
int32 ImageCompressionQuality = 0;
|
2022-09-26 15:12:13 -04:00
|
|
|
OP::ImageLayerArgs Args;
|
2023-05-24 02:50:03 -04:00
|
|
|
Ptr<const Image> Blended;
|
|
|
|
|
Ptr<const Image> Mask;
|
|
|
|
|
Ptr<Image> Result;
|
|
|
|
|
EImageFormat InitialFormat = EImageFormat::IF_NONE;
|
2022-09-26 15:12:13 -04:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
2023-05-24 02:50:03 -04:00
|
|
|
bool FImageLayerTask::Prepare(CodeRunner* Runner, bool& bOutFailed)
|
2022-09-26 15:12:13 -04:00
|
|
|
{
|
2023-05-24 02:50:03 -04:00
|
|
|
// This runs in the mutable Runner thread
|
|
|
|
|
MUTABLE_CPUPROFILER_SCOPE(FImageLayerTask_Prepare);
|
|
|
|
|
bOutFailed = false;
|
2022-12-05 05:01:41 -05:00
|
|
|
|
2023-05-24 02:50:03 -04:00
|
|
|
ImageCompressionQuality = Runner->m_pSettings->ImageCompressionQuality;
|
|
|
|
|
|
|
|
|
|
Ptr<const Image> Base = Runner->LoadImage({ Args.base, Op.ExecutionIndex, Op.ExecutionOptions });
|
|
|
|
|
check(!Base || Base->GetFormat() < EImageFormat::IF_COUNT);
|
|
|
|
|
|
|
|
|
|
Blended = Runner->LoadImage({ Args.blended, Op.ExecutionIndex, Op.ExecutionOptions });
|
2022-09-26 15:12:13 -04:00
|
|
|
if (Args.mask)
|
|
|
|
|
{
|
2023-05-24 02:50:03 -04:00
|
|
|
Mask = Runner->LoadImage({ Args.mask, Op.ExecutionIndex, Op.ExecutionOptions });
|
|
|
|
|
check(!Mask || Mask->GetFormat() < EImageFormat::IF_COUNT);
|
2022-09-26 15:12:13 -04:00
|
|
|
}
|
2023-05-24 02:50:03 -04:00
|
|
|
|
|
|
|
|
// Shortcuts
|
|
|
|
|
if (!Base)
|
|
|
|
|
{
|
|
|
|
|
Runner->Release(Blended);
|
|
|
|
|
Runner->Release(Mask);
|
|
|
|
|
Runner->StoreImage(Op, Base);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool bValid = Base->GetSizeX() > 0 && Base->GetSizeY() > 0;
|
|
|
|
|
if (!bValid || !Blended)
|
|
|
|
|
{
|
|
|
|
|
Runner->Release(Blended);
|
|
|
|
|
Runner->Release(Mask);
|
|
|
|
|
Runner->StoreImage(Op, Base);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2023-06-23 02:01:13 -04:00
|
|
|
FImageOperator ImOp = MakeImageOperator(Runner);
|
|
|
|
|
|
2023-05-24 02:50:03 -04:00
|
|
|
// Input data fixes
|
|
|
|
|
InitialFormat = Base->GetFormat();
|
|
|
|
|
|
|
|
|
|
if (IsCompressedFormat(InitialFormat))
|
|
|
|
|
{
|
|
|
|
|
EImageFormat UncompressedFormat = GetUncompressedFormat(InitialFormat);
|
|
|
|
|
Ptr<Image> Formatted = Runner->CreateImage( Base->GetSizeX(), Base->GetSizeY(), Base->GetLODCount(), UncompressedFormat, EInitializationType::NotInitialized );
|
|
|
|
|
bool bSuccess = false;
|
2023-06-23 02:01:13 -04:00
|
|
|
ImOp.ImagePixelFormat(bSuccess, ImageCompressionQuality, Formatted.get(), Base.get());
|
2023-05-24 02:50:03 -04:00
|
|
|
check(bSuccess); // Decompression cannot fail
|
|
|
|
|
Runner->Release(Base);
|
|
|
|
|
Base = Formatted;
|
|
|
|
|
}
|
|
|
|
|
|
2023-11-20 04:22:10 -05:00
|
|
|
bool bMustHaveSameFormat = !(Args.flags & (OP::ImageLayerArgs::F_BASE_RGB_FROM_ALPHA | OP::ImageLayerArgs::F_BLENDED_RGB_FROM_ALPHA));
|
|
|
|
|
if (Blended && InitialFormat != Blended->GetFormat() && bMustHaveSameFormat)
|
2023-05-24 02:50:03 -04:00
|
|
|
{
|
|
|
|
|
Ptr<Image> Formatted = Runner->CreateImage(Blended->GetSizeX(), Blended->GetSizeY(), Blended->GetLODCount(), Base->GetFormat(), EInitializationType::NotInitialized);
|
|
|
|
|
bool bSuccess = false;
|
2023-06-23 02:01:13 -04:00
|
|
|
ImOp.ImagePixelFormat(bSuccess, ImageCompressionQuality, Formatted.get(), Blended.get());
|
2023-05-24 02:50:03 -04:00
|
|
|
check(bSuccess); // Decompression cannot fail
|
|
|
|
|
Runner->Release(Blended);
|
|
|
|
|
Blended = Formatted;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (Base->GetSize() != Blended->GetSize())
|
|
|
|
|
{
|
|
|
|
|
MUTABLE_CPUPROFILER_SCOPE(ImageResize_EmergencyFix);
|
|
|
|
|
Ptr<Image> Resized = Runner->CreateImage(Base->GetSizeX(), Base->GetSizeY(), 1, Blended->GetFormat(), EInitializationType::NotInitialized);
|
2023-06-23 02:01:13 -04:00
|
|
|
ImOp.ImageResizeLinear(Resized.get(), ImageCompressionQuality, Blended.get());
|
2023-05-24 02:50:03 -04:00
|
|
|
Runner->Release(Blended);
|
|
|
|
|
Blended = Resized;
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-12 05:31:15 -05:00
|
|
|
if (Mask)
|
2023-05-24 02:50:03 -04:00
|
|
|
{
|
|
|
|
|
if (Base->GetSize() != Mask->GetSize())
|
|
|
|
|
{
|
|
|
|
|
MUTABLE_CPUPROFILER_SCOPE(ImageResize_EmergencyFix);
|
|
|
|
|
Ptr<Image> Resized = Runner->CreateImage(Base->GetSizeX(), Base->GetSizeY(), 1, Mask->GetFormat(), EInitializationType::NotInitialized);
|
2023-06-23 02:01:13 -04:00
|
|
|
ImOp.ImageResizeLinear(Resized.get(), ImageCompressionQuality, Mask.get());
|
2023-05-24 02:50:03 -04:00
|
|
|
Runner->Release(Mask);
|
|
|
|
|
Mask = Resized;
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// emergy fix c36adf47-e40d-490f-b709-41142bafad78
|
|
|
|
|
if (Mask->GetLODCount() < Base->GetLODCount())
|
|
|
|
|
{
|
|
|
|
|
MUTABLE_CPUPROFILER_SCOPE(ImageLayer_EmergencyFix);
|
|
|
|
|
|
2024-03-12 07:17:49 -04:00
|
|
|
int32 StartLevel = Mask->GetLODCount() - 1;
|
|
|
|
|
int32 LevelCount = Base->GetLODCount();
|
2023-05-24 02:50:03 -04:00
|
|
|
|
2024-06-14 04:01:28 -04:00
|
|
|
// Uncompress mask to avoid excessive RLE-compression and decompression in the following ops.
|
|
|
|
|
Ptr<Image> UncompressedMask = Runner->CreateImage(Mask->GetSizeX(), Mask->GetSizeY(), Mask->GetLODCount(), GetUncompressedFormat(Mask->GetFormat()), EInitializationType::NotInitialized);
|
|
|
|
|
bool bSuccess = false;
|
|
|
|
|
ImOp.ImagePixelFormat(bSuccess, ImageCompressionQuality, UncompressedMask.get(), Mask.get());
|
|
|
|
|
|
|
|
|
|
Ptr<Image> MaskFix = UncompressedMask;
|
2024-03-12 07:17:49 -04:00
|
|
|
MaskFix->DataStorage.SetNumLODs(LevelCount);
|
2023-05-24 02:50:03 -04:00
|
|
|
|
2024-03-12 07:17:49 -04:00
|
|
|
FMipmapGenerationSettings Settings{};
|
|
|
|
|
ImOp.ImageMipmap(ImageCompressionQuality, MaskFix.get(), MaskFix.get(), StartLevel, LevelCount, Settings);
|
|
|
|
|
|
2024-06-14 04:01:28 -04:00
|
|
|
Runner->Release(Mask);
|
2024-03-12 07:17:49 -04:00
|
|
|
Mask = MaskFix;
|
2023-05-24 02:50:03 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Create destination data
|
|
|
|
|
Result = Runner->CloneOrTakeOver(Base);
|
|
|
|
|
return true;
|
2022-09-26 15:12:13 -04:00
|
|
|
}
|
|
|
|
|
|
2023-05-24 02:50:03 -04:00
|
|
|
|
2022-09-26 15:12:13 -04:00
|
|
|
void FImageLayerTask::DoWork()
|
|
|
|
|
{
|
|
|
|
|
MUTABLE_CPUPROFILER_SCOPE(FImageLayerTask);
|
|
|
|
|
|
2023-05-24 02:50:03 -04:00
|
|
|
// This runs in a worker thread.
|
2022-09-26 15:12:13 -04:00
|
|
|
|
2023-05-24 02:50:03 -04:00
|
|
|
bool bOnlyOneMip = false;
|
|
|
|
|
if (Blended->GetLODCount() < Result->GetLODCount())
|
2022-11-11 14:56:03 -05:00
|
|
|
{
|
2023-05-24 02:50:03 -04:00
|
|
|
bOnlyOneMip = true;
|
2022-11-11 14:56:03 -05:00
|
|
|
}
|
|
|
|
|
|
2023-05-24 02:50:03 -04:00
|
|
|
bool bDone = false;
|
2022-12-05 05:01:41 -05:00
|
|
|
|
2023-05-24 02:50:03 -04:00
|
|
|
if (!Mask
|
|
|
|
|
&&
|
|
|
|
|
// Flags have to match exactly for this optimize case. Other flags are not supported.
|
2023-09-13 06:59:56 -04:00
|
|
|
Args.flags == OP::ImageLayerArgs::F_USE_MASK_FROM_BLENDED
|
2023-05-24 02:50:03 -04:00
|
|
|
&&
|
|
|
|
|
Args.blendType == uint8(EBlendType::BT_BLEND)
|
|
|
|
|
&&
|
|
|
|
|
Args.blendTypeAlpha == uint8(EBlendType::BT_LIGHTEN))
|
2022-09-26 15:12:13 -04:00
|
|
|
{
|
2023-05-24 02:50:03 -04:00
|
|
|
MUTABLE_CPUPROFILER_SCOPE(ImageLayerTask_Optimized);
|
2022-09-26 15:12:13 -04:00
|
|
|
|
2023-05-24 02:50:03 -04:00
|
|
|
// This is a frequent critical-path case because of multilayer projectors.
|
|
|
|
|
bDone = true;
|
2022-09-26 15:12:13 -04:00
|
|
|
|
2023-05-24 02:50:03 -04:00
|
|
|
constexpr bool bUseVectorImplementation = false;
|
|
|
|
|
if constexpr (bUseVectorImplementation)
|
2022-09-26 15:12:13 -04:00
|
|
|
{
|
2023-05-24 02:50:03 -04:00
|
|
|
BufferLayerCompositeVector<VectorBlendChannelMasked, VectorLightenChannel, false>(Result.get(), Blended.get(), bOnlyOneMip, Args.BlendAlphaSourceChannel);
|
2022-09-26 15:12:13 -04:00
|
|
|
}
|
2023-05-24 02:50:03 -04:00
|
|
|
else
|
2022-09-26 15:12:13 -04:00
|
|
|
{
|
2023-05-24 02:50:03 -04:00
|
|
|
BufferLayerComposite<BlendChannelMasked, LightenChannel, false>(Result.get(), Blended.get(), bOnlyOneMip, Args.BlendAlphaSourceChannel);
|
2023-01-18 10:52:46 -05:00
|
|
|
}
|
2022-09-26 15:12:13 -04:00
|
|
|
}
|
|
|
|
|
|
2023-05-24 02:50:03 -04:00
|
|
|
bool bApplyColorBlendToAlpha = (Args.flags & OP::ImageLayerArgs::F_APPLY_TO_ALPHA) != 0;
|
|
|
|
|
bool bUseBlendSourceFromBlendAlpha = (Args.flags & OP::ImageLayerArgs::F_BLENDED_RGB_FROM_ALPHA) != 0;
|
|
|
|
|
bool bUseMaskFromBlendAlpha = (Args.flags & OP::ImageLayerArgs::F_USE_MASK_FROM_BLENDED);
|
|
|
|
|
|
2024-02-12 05:31:15 -05:00
|
|
|
if (!bDone && Mask)
|
2022-09-26 15:12:13 -04:00
|
|
|
{
|
2023-05-24 02:50:03 -04:00
|
|
|
// Not implemented yet
|
|
|
|
|
check(!bUseBlendSourceFromBlendAlpha);
|
|
|
|
|
|
|
|
|
|
// TODO: in-place
|
|
|
|
|
|
|
|
|
|
switch (EBlendType(Args.blendType))
|
|
|
|
|
{
|
|
|
|
|
case EBlendType::BT_NORMAL_COMBINE: ImageNormalCombine(Result.get(), Result.get(), Mask.get(), Blended.get(), bOnlyOneMip); break;
|
2024-03-12 07:17:49 -04:00
|
|
|
case EBlendType::BT_SOFTLIGHT: BufferLayer<SoftLightChannelMasked, SoftLightChannel, true>(Result.get(), Result.get(), Mask.get(), Blended.get(), bApplyColorBlendToAlpha, bOnlyOneMip); break;
|
|
|
|
|
case EBlendType::BT_HARDLIGHT: BufferLayer<HardLightChannelMasked, HardLightChannel, true>(Result.get(), Result.get(), Mask.get(), Blended.get(), bApplyColorBlendToAlpha, bOnlyOneMip); break;
|
|
|
|
|
case EBlendType::BT_BURN: BufferLayer<BurnChannelMasked, BurnChannel, true>(Result.get(), Result.get(), Mask.get(), Blended.get(), bApplyColorBlendToAlpha, bOnlyOneMip); break;
|
|
|
|
|
case EBlendType::BT_DODGE: BufferLayer<DodgeChannelMasked, DodgeChannel, true>(Result.get(), Result.get(), Mask.get(), Blended.get(), bApplyColorBlendToAlpha, bOnlyOneMip); break;
|
|
|
|
|
case EBlendType::BT_SCREEN: BufferLayer<ScreenChannelMasked, ScreenChannel, true>(Result.get(), Result.get(), Mask.get(), Blended.get(), bApplyColorBlendToAlpha, bOnlyOneMip); break;
|
|
|
|
|
case EBlendType::BT_OVERLAY: BufferLayer<OverlayChannelMasked, OverlayChannel, true>(Result.get(), Result.get(), Mask.get(), Blended.get(), bApplyColorBlendToAlpha, bOnlyOneMip); break;
|
|
|
|
|
case EBlendType::BT_LIGHTEN: BufferLayer<LightenChannelMasked, LightenChannel, true>(Result.get(), Result.get(), Mask.get(), Blended.get(), bApplyColorBlendToAlpha, bOnlyOneMip); break;
|
|
|
|
|
case EBlendType::BT_MULTIPLY: BufferLayer<MultiplyChannelMasked, MultiplyChannel, true>(Result.get(), Result.get(), Mask.get(), Blended.get(), bApplyColorBlendToAlpha, bOnlyOneMip); break;
|
|
|
|
|
case EBlendType::BT_BLEND: BufferLayer<BlendChannelMasked, BlendChannel, true>(Result.get(), Result.get(), Mask.get(), Blended.get(), bApplyColorBlendToAlpha, bOnlyOneMip); break;
|
2023-05-24 02:50:03 -04:00
|
|
|
case EBlendType::BT_NONE: break;
|
|
|
|
|
default: check(false);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
else if (!bDone && bUseMaskFromBlendAlpha)
|
|
|
|
|
{
|
|
|
|
|
// Not implemented yet
|
|
|
|
|
check(!bUseBlendSourceFromBlendAlpha);
|
|
|
|
|
|
|
|
|
|
// Apply blend without to RGB using mask in blended alpha
|
|
|
|
|
switch (EBlendType(Args.blendType))
|
|
|
|
|
{
|
|
|
|
|
case EBlendType::BT_NORMAL_COMBINE: check(false); break;
|
2024-03-12 07:17:49 -04:00
|
|
|
case EBlendType::BT_SOFTLIGHT: BufferLayerEmbeddedMask<SoftLightChannelMasked, SoftLightChannel, false>(Result.get(), Result.get(), Blended.get(), bApplyColorBlendToAlpha, bOnlyOneMip); break;
|
|
|
|
|
case EBlendType::BT_HARDLIGHT: BufferLayerEmbeddedMask<HardLightChannelMasked, HardLightChannel, false>(Result.get(), Result.get(), Blended.get(), bApplyColorBlendToAlpha, bOnlyOneMip); break;
|
|
|
|
|
case EBlendType::BT_BURN: BufferLayerEmbeddedMask<BurnChannelMasked, BurnChannel, false>(Result.get(), Result.get(), Blended.get(), bApplyColorBlendToAlpha, bOnlyOneMip); break;
|
|
|
|
|
case EBlendType::BT_DODGE: BufferLayerEmbeddedMask<DodgeChannelMasked, DodgeChannel, false>(Result.get(), Result.get(), Blended.get(), bApplyColorBlendToAlpha, bOnlyOneMip); break;
|
|
|
|
|
case EBlendType::BT_SCREEN: BufferLayerEmbeddedMask<ScreenChannelMasked, ScreenChannel, false>(Result.get(), Result.get(), Blended.get(), bApplyColorBlendToAlpha, bOnlyOneMip); break;
|
|
|
|
|
case EBlendType::BT_OVERLAY: BufferLayerEmbeddedMask<OverlayChannelMasked, OverlayChannel, false>(Result.get(), Result.get(), Blended.get(), bApplyColorBlendToAlpha, bOnlyOneMip); break;
|
|
|
|
|
case EBlendType::BT_LIGHTEN: BufferLayerEmbeddedMask<LightenChannelMasked, LightenChannel, false>(Result.get(), Result.get(), Blended.get(), bApplyColorBlendToAlpha, bOnlyOneMip); break;
|
|
|
|
|
case EBlendType::BT_MULTIPLY: BufferLayerEmbeddedMask<MultiplyChannelMasked, MultiplyChannel, false>(Result.get(), Result.get(), Blended.get(), bApplyColorBlendToAlpha, bOnlyOneMip); break;
|
|
|
|
|
case EBlendType::BT_BLEND: BufferLayerEmbeddedMask<BlendChannelMasked, BlendChannel, false>(Result.get(), Result.get(), Blended.get(), bApplyColorBlendToAlpha, bOnlyOneMip); break;
|
2023-05-24 02:50:03 -04:00
|
|
|
case EBlendType::BT_NONE: break;
|
|
|
|
|
default: check(false);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (!bDone)
|
|
|
|
|
{
|
|
|
|
|
// Apply blend without mask to RGB
|
|
|
|
|
switch (EBlendType(Args.blendType))
|
|
|
|
|
{
|
|
|
|
|
case EBlendType::BT_NORMAL_COMBINE: ImageNormalCombine(Result.get(), Result.get(), Blended.get(), bOnlyOneMip); check(!bUseBlendSourceFromBlendAlpha); break;
|
|
|
|
|
case EBlendType::BT_SOFTLIGHT: BufferLayer<SoftLightChannel, true>(Result.get(), Result.get(), Blended.get(), bApplyColorBlendToAlpha, bOnlyOneMip, bUseBlendSourceFromBlendAlpha); break;
|
|
|
|
|
case EBlendType::BT_HARDLIGHT: BufferLayer<HardLightChannel, true>(Result.get(), Result.get(), Blended.get(), bApplyColorBlendToAlpha, bOnlyOneMip, bUseBlendSourceFromBlendAlpha); break;
|
|
|
|
|
case EBlendType::BT_BURN: BufferLayer<BurnChannel, true>(Result.get(), Result.get(), Blended.get(), bApplyColorBlendToAlpha, bOnlyOneMip, bUseBlendSourceFromBlendAlpha); break;
|
|
|
|
|
case EBlendType::BT_DODGE: BufferLayer<DodgeChannel, true>(Result.get(), Result.get(), Blended.get(), bApplyColorBlendToAlpha, bOnlyOneMip, bUseBlendSourceFromBlendAlpha); break;
|
|
|
|
|
case EBlendType::BT_SCREEN: BufferLayer<ScreenChannel, true>(Result.get(), Result.get(), Blended.get(), bApplyColorBlendToAlpha, bOnlyOneMip, bUseBlendSourceFromBlendAlpha); break;
|
|
|
|
|
case EBlendType::BT_OVERLAY: BufferLayer<OverlayChannel, true>(Result.get(), Result.get(), Blended.get(), bApplyColorBlendToAlpha, bOnlyOneMip, bUseBlendSourceFromBlendAlpha); break;
|
|
|
|
|
case EBlendType::BT_LIGHTEN: BufferLayer<LightenChannel, true>(Result.get(), Result.get(), Blended.get(), bApplyColorBlendToAlpha, bOnlyOneMip, bUseBlendSourceFromBlendAlpha); break;
|
|
|
|
|
case EBlendType::BT_MULTIPLY: BufferLayer<MultiplyChannel, true>(Result.get(), Result.get(), Blended.get(), bApplyColorBlendToAlpha, bOnlyOneMip, bUseBlendSourceFromBlendAlpha); break;
|
|
|
|
|
case EBlendType::BT_BLEND: BufferLayer<BlendChannel, true>(Result.get(), Result.get(), Blended.get(), bApplyColorBlendToAlpha, bOnlyOneMip, bUseBlendSourceFromBlendAlpha); break;
|
|
|
|
|
case EBlendType::BT_NONE: break;
|
|
|
|
|
default: check(false);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Apply the separate blend operation for alpha
|
|
|
|
|
if (!bDone && !bApplyColorBlendToAlpha)
|
|
|
|
|
{
|
|
|
|
|
// Separate alpha operation ignores the mask.
|
|
|
|
|
switch (EBlendType(Args.blendTypeAlpha))
|
|
|
|
|
{
|
|
|
|
|
case EBlendType::BT_SOFTLIGHT: BufferLayerInPlace<SoftLightChannel, false, 1>(Result.get(), Blended.get(), bOnlyOneMip, 3, Args.BlendAlphaSourceChannel); break;
|
|
|
|
|
case EBlendType::BT_HARDLIGHT: BufferLayerInPlace<HardLightChannel, false, 1>(Result.get(), Blended.get(), bOnlyOneMip, 3, Args.BlendAlphaSourceChannel); break;
|
|
|
|
|
case EBlendType::BT_BURN: BufferLayerInPlace<BurnChannel, false, 1>(Result.get(), Blended.get(), bOnlyOneMip, 3, Args.BlendAlphaSourceChannel); break;
|
|
|
|
|
case EBlendType::BT_DODGE: BufferLayerInPlace<DodgeChannel, false, 1>(Result.get(), Blended.get(), bOnlyOneMip, 3, Args.BlendAlphaSourceChannel); break;
|
|
|
|
|
case EBlendType::BT_SCREEN: BufferLayerInPlace<ScreenChannel, false, 1>(Result.get(), Blended.get(), bOnlyOneMip, 3, Args.BlendAlphaSourceChannel); break;
|
|
|
|
|
case EBlendType::BT_OVERLAY: BufferLayerInPlace<OverlayChannel, false, 1>(Result.get(), Blended.get(), bOnlyOneMip, 3, Args.BlendAlphaSourceChannel); break;
|
|
|
|
|
case EBlendType::BT_LIGHTEN: BufferLayerInPlace<LightenChannel, false, 1>(Result.get(), Blended.get(), bOnlyOneMip, 3, Args.BlendAlphaSourceChannel); break;
|
|
|
|
|
case EBlendType::BT_MULTIPLY: BufferLayerInPlace<MultiplyChannel, false, 1>(Result.get(), Blended.get(), bOnlyOneMip, 3, Args.BlendAlphaSourceChannel); break;
|
|
|
|
|
case EBlendType::BT_BLEND: BufferLayerInPlace<BlendChannel, false, 1>(Result.get(), Blended.get(), bOnlyOneMip, 3, Args.BlendAlphaSourceChannel); break;
|
|
|
|
|
case EBlendType::BT_NONE: break;
|
|
|
|
|
default: check(false);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (bOnlyOneMip)
|
|
|
|
|
{
|
|
|
|
|
MUTABLE_CPUPROFILER_SCOPE(ImageLayer_MipFix);
|
|
|
|
|
FMipmapGenerationSettings DummyMipSettings{};
|
|
|
|
|
ImageMipmapInPlace(ImageCompressionQuality, Result.get(), DummyMipSettings);
|
2022-09-26 15:12:13 -04:00
|
|
|
}
|
|
|
|
|
|
2023-02-08 08:22:43 -05:00
|
|
|
// Reset relevancy map.
|
2023-05-24 02:50:03 -04:00
|
|
|
Result->m_flags &= ~Image::EImageFlags::IF_HAS_RELEVANCY_MAP;
|
2022-09-26 15:12:13 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2024-06-14 04:01:28 -04:00
|
|
|
bool FImageLayerTask::Complete( CodeRunner* Runner )
|
2022-09-26 15:12:13 -04:00
|
|
|
{
|
2023-05-24 02:50:03 -04:00
|
|
|
// This runs in the Runner thread
|
|
|
|
|
Runner->Release(Blended);
|
|
|
|
|
Runner->Release(Mask);
|
|
|
|
|
|
|
|
|
|
// If no shortcut was taken
|
|
|
|
|
if (Result)
|
|
|
|
|
{
|
|
|
|
|
if (InitialFormat != Result->GetFormat())
|
|
|
|
|
{
|
|
|
|
|
Ptr<Image> Formatted = Runner->CreateImage(Result->GetSizeX(), Result->GetSizeY(), Result->GetLODCount(), InitialFormat, EInitializationType::NotInitialized);
|
|
|
|
|
bool bSuccess = false;
|
2023-06-23 02:01:13 -04:00
|
|
|
|
|
|
|
|
FImageOperator ImOp = MakeImageOperator(Runner);
|
|
|
|
|
ImOp.ImagePixelFormat(bSuccess, ImageCompressionQuality, Formatted.get(), Result.get());
|
2023-05-24 02:50:03 -04:00
|
|
|
check(bSuccess);
|
|
|
|
|
|
|
|
|
|
Runner->Release(Result);
|
|
|
|
|
Result = Formatted;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Runner->StoreImage(Op, Result);
|
|
|
|
|
}
|
2024-06-14 04:01:28 -04:00
|
|
|
|
|
|
|
|
bool bSuccess = true;
|
|
|
|
|
return bSuccess;
|
2022-09-26 15:12:13 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//---------------------------------------------------------------------------------------------
|
|
|
|
|
//---------------------------------------------------------------------------------------------
|
|
|
|
|
//---------------------------------------------------------------------------------------------
|
|
|
|
|
class FImageLayerColourTask : public CodeRunner::FIssuedTask
|
|
|
|
|
{
|
|
|
|
|
public:
|
2023-05-24 02:50:03 -04:00
|
|
|
FImageLayerColourTask(const FScheduledOp& InOp, const OP::ImageLayerColourArgs& InArgs)
|
|
|
|
|
: FIssuedTask(InOp), Args(InArgs)
|
|
|
|
|
{}
|
2022-09-26 15:12:13 -04:00
|
|
|
|
|
|
|
|
// FIssuedTask interface
|
2024-06-14 04:01:28 -04:00
|
|
|
virtual bool Prepare(CodeRunner*, bool& bOutFailed) override;
|
|
|
|
|
virtual void DoWork() override;
|
|
|
|
|
virtual bool Complete(CodeRunner*) override;
|
2022-09-26 15:12:13 -04:00
|
|
|
|
|
|
|
|
private:
|
2023-05-24 02:50:03 -04:00
|
|
|
int32 ImageCompressionQuality = 0;
|
2022-09-26 15:12:13 -04:00
|
|
|
OP::ImageLayerColourArgs Args;
|
2023-05-24 02:50:03 -04:00
|
|
|
FVector4f Color;
|
|
|
|
|
Ptr<const Image> Mask;
|
|
|
|
|
Ptr<Image> Result;
|
|
|
|
|
EImageFormat InitialFormat = EImageFormat::IF_NONE;
|
2022-09-26 15:12:13 -04:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
2023-05-24 02:50:03 -04:00
|
|
|
bool FImageLayerColourTask::Prepare(CodeRunner* Runner, bool& bOutFailed)
|
2022-09-26 15:12:13 -04:00
|
|
|
{
|
2023-05-24 02:50:03 -04:00
|
|
|
// This runs in the mutable Runner thread
|
|
|
|
|
MUTABLE_CPUPROFILER_SCOPE(FImageLayerColourTask_Prepare);
|
|
|
|
|
bOutFailed = false;
|
2022-12-05 05:01:41 -05:00
|
|
|
|
2023-05-24 02:50:03 -04:00
|
|
|
ImageCompressionQuality = Runner->m_pSettings->ImageCompressionQuality;
|
|
|
|
|
|
|
|
|
|
Ptr<const Image> Base = Runner->LoadImage({ Args.base, Op.ExecutionIndex, Op.ExecutionOptions });
|
|
|
|
|
check(!Base || Base->GetFormat() < EImageFormat::IF_COUNT);
|
|
|
|
|
|
|
|
|
|
Color = Runner->LoadColor({ Args.colour, Op.ExecutionIndex, 0 });
|
2022-09-26 15:12:13 -04:00
|
|
|
if (Args.mask)
|
|
|
|
|
{
|
2023-05-24 02:50:03 -04:00
|
|
|
Mask = Runner->LoadImage({ Args.mask, Op.ExecutionIndex, Op.ExecutionOptions });
|
|
|
|
|
check(!Mask || Mask->GetFormat() < EImageFormat::IF_COUNT);
|
2022-09-26 15:12:13 -04:00
|
|
|
}
|
|
|
|
|
|
2023-05-24 02:50:03 -04:00
|
|
|
// Shortcuts
|
|
|
|
|
if (!Base)
|
2022-11-24 10:06:46 -05:00
|
|
|
{
|
2023-05-24 02:50:03 -04:00
|
|
|
Runner->Release(Mask);
|
|
|
|
|
Runner->StoreImage(Op, Base);
|
|
|
|
|
return false;
|
2022-11-24 10:06:46 -05:00
|
|
|
}
|
2022-12-05 05:01:41 -05:00
|
|
|
|
2023-05-24 02:50:03 -04:00
|
|
|
bool bValid = Base->GetSizeX() > 0 && Base->GetSizeY() > 0;
|
|
|
|
|
if (!bValid)
|
|
|
|
|
{
|
|
|
|
|
Runner->Release(Mask);
|
|
|
|
|
Runner->StoreImage(Op, Base);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Fix input data
|
|
|
|
|
InitialFormat = Base->GetFormat();
|
2022-12-05 05:01:41 -05:00
|
|
|
check(InitialFormat < EImageFormat::IF_COUNT);
|
|
|
|
|
|
2023-05-24 02:50:03 -04:00
|
|
|
if (Args.mask && Mask)
|
2022-09-26 15:12:13 -04:00
|
|
|
{
|
2023-06-23 02:01:13 -04:00
|
|
|
FImageOperator ImOp = MakeImageOperator(Runner);
|
|
|
|
|
|
2023-05-24 02:50:03 -04:00
|
|
|
if (Base->GetSize() != Mask->GetSize())
|
2022-09-26 15:12:13 -04:00
|
|
|
{
|
2023-01-27 14:09:59 -05:00
|
|
|
MUTABLE_CPUPROFILER_SCOPE(ImageResize_EmergencyFixSize);
|
2023-05-24 02:50:03 -04:00
|
|
|
Ptr<Image> Resized = Runner->CreateImage(Base->GetSizeX(), Base->GetSizeY(), 1, Mask->GetFormat(), EInitializationType::NotInitialized);
|
2023-06-23 02:01:13 -04:00
|
|
|
ImOp.ImageResizeLinear(Resized.get(), ImageCompressionQuality, Mask.get() );
|
2023-05-24 02:50:03 -04:00
|
|
|
Runner->Release(Mask);
|
|
|
|
|
Mask = Resized;
|
2022-09-26 15:12:13 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// emergy fix c36adf47-e40d-490f-b709-41142bafad78
|
2023-05-24 02:50:03 -04:00
|
|
|
if (Mask->GetLODCount() < Base->GetLODCount())
|
2022-09-26 15:12:13 -04:00
|
|
|
{
|
2023-01-27 14:09:59 -05:00
|
|
|
MUTABLE_CPUPROFILER_SCOPE(ImageResize_EmergencyFixMips);
|
2024-03-12 07:17:49 -04:00
|
|
|
int32 StartLevel = Mask->GetLODCount() - 1;
|
|
|
|
|
int32 LevelCount = Base->GetLODCount();
|
2022-09-26 15:12:13 -04:00
|
|
|
|
2024-03-12 07:17:49 -04:00
|
|
|
Ptr<Image> MaskFix = Runner->CloneOrTakeOver(Mask);
|
|
|
|
|
MaskFix->DataStorage.SetNumLODs(LevelCount);
|
2022-09-26 15:12:13 -04:00
|
|
|
|
2024-03-12 07:17:49 -04:00
|
|
|
FMipmapGenerationSettings Settings{};
|
|
|
|
|
ImOp.ImageMipmap(ImageCompressionQuality, MaskFix.get(), MaskFix.get(), StartLevel, LevelCount, Settings);
|
|
|
|
|
|
|
|
|
|
Mask = MaskFix;
|
2022-09-26 15:12:13 -04:00
|
|
|
}
|
|
|
|
|
}
|
2022-11-21 09:44:42 -05:00
|
|
|
|
2023-05-24 02:50:03 -04:00
|
|
|
// Create destination data
|
|
|
|
|
Result = Runner->CloneOrTakeOver(Base);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void FImageLayerColourTask::DoWork()
|
|
|
|
|
{
|
|
|
|
|
// This runs in a worker thread
|
|
|
|
|
MUTABLE_CPUPROFILER_SCOPE(FImageLayerColourTask);
|
|
|
|
|
|
2022-11-21 09:44:42 -05:00
|
|
|
bool bOnlyOneMip = false;
|
|
|
|
|
|
|
|
|
|
// Does it apply to colour?
|
|
|
|
|
if (EBlendType(Args.blendType) != EBlendType::BT_NONE)
|
2022-09-26 15:12:13 -04:00
|
|
|
{
|
2022-12-12 04:38:14 -05:00
|
|
|
// TODO: It could be done "in-place"
|
2023-05-24 02:50:03 -04:00
|
|
|
if (Args.mask && Mask)
|
2022-11-21 09:44:42 -05:00
|
|
|
{
|
2023-01-11 14:28:53 -05:00
|
|
|
// Not implemented yet
|
|
|
|
|
check(Args.flags==0);
|
|
|
|
|
|
2022-11-21 09:44:42 -05:00
|
|
|
// \todo: precalculated tables for softlight
|
|
|
|
|
switch (EBlendType(Args.blendType))
|
|
|
|
|
{
|
2023-05-24 02:50:03 -04:00
|
|
|
case EBlendType::BT_NORMAL_COMBINE: ImageNormalCombine(Result.get(), Result.get(), Mask.get(), Color); break;
|
2024-03-12 07:17:49 -04:00
|
|
|
case EBlendType::BT_SOFTLIGHT: BufferLayerColour<SoftLightChannelMasked, SoftLightChannel>(Result.get(), Result.get(), Mask.get(), Color); break;
|
|
|
|
|
case EBlendType::BT_HARDLIGHT: BufferLayerColour<HardLightChannelMasked, HardLightChannel>(Result.get(), Result.get(), Mask.get(), Color); break;
|
|
|
|
|
case EBlendType::BT_BURN: BufferLayerColour<BurnChannelMasked, BurnChannel>(Result.get(), Result.get(), Mask.get(), Color); break;
|
|
|
|
|
case EBlendType::BT_DODGE: BufferLayerColour<DodgeChannelMasked, DodgeChannel>(Result.get(), Result.get(), Mask.get(), Color); break;
|
|
|
|
|
case EBlendType::BT_SCREEN: BufferLayerColour<ScreenChannelMasked, ScreenChannel>(Result.get(), Result.get(), Mask.get(), Color); break;
|
|
|
|
|
case EBlendType::BT_OVERLAY: BufferLayerColour<OverlayChannelMasked, OverlayChannel>(Result.get(), Result.get(), Mask.get(), Color); break;
|
|
|
|
|
case EBlendType::BT_LIGHTEN: BufferLayerColour<LightenChannelMasked, LightenChannel>(Result.get(), Result.get(), Mask.get(), Color); break;
|
|
|
|
|
case EBlendType::BT_MULTIPLY: BufferLayerColour<MultiplyChannelMasked, MultiplyChannel>(Result.get(), Result.get(), Mask.get(), Color); break;
|
|
|
|
|
case EBlendType::BT_BLEND: BufferLayerColour<BlendChannelMasked, BlendChannel>(Result.get(), Result.get(), Mask.get(), Color); break;
|
2022-11-21 09:44:42 -05:00
|
|
|
default: check(false);
|
|
|
|
|
}
|
|
|
|
|
|
2022-09-26 15:12:13 -04:00
|
|
|
}
|
2022-11-21 09:44:42 -05:00
|
|
|
else
|
|
|
|
|
{
|
2023-01-11 14:28:53 -05:00
|
|
|
if (Args.flags & OP::ImageLayerArgs::FLAGS::F_BASE_RGB_FROM_ALPHA)
|
2022-11-21 09:44:42 -05:00
|
|
|
{
|
2023-01-11 14:28:53 -05:00
|
|
|
switch (EBlendType(Args.blendType))
|
|
|
|
|
{
|
|
|
|
|
case EBlendType::BT_NORMAL_COMBINE: check(false); break;
|
2023-05-24 02:50:03 -04:00
|
|
|
case EBlendType::BT_SOFTLIGHT: BufferLayerColourFromAlpha<SoftLightChannel>(Result.get(), Result.get(), Color); break;
|
|
|
|
|
case EBlendType::BT_HARDLIGHT: BufferLayerColourFromAlpha<HardLightChannel>(Result.get(), Result.get(), Color); break;
|
|
|
|
|
case EBlendType::BT_BURN: BufferLayerColourFromAlpha<BurnChannel>(Result.get(), Result.get(), Color); break;
|
|
|
|
|
case EBlendType::BT_DODGE: BufferLayerColourFromAlpha<DodgeChannel>(Result.get(), Result.get(), Color); break;
|
|
|
|
|
case EBlendType::BT_SCREEN: BufferLayerColourFromAlpha<ScreenChannel>(Result.get(), Result.get(), Color); break;
|
|
|
|
|
case EBlendType::BT_OVERLAY: BufferLayerColourFromAlpha<OverlayChannel>(Result.get(), Result.get(), Color); break;
|
|
|
|
|
case EBlendType::BT_LIGHTEN: BufferLayerColourFromAlpha<LightenChannel>(Result.get(), Result.get(), Color); break;
|
|
|
|
|
case EBlendType::BT_MULTIPLY: BufferLayerColourFromAlpha<MultiplyChannel>(Result.get(), Result.get(), Color); break;
|
2023-01-11 14:28:53 -05:00
|
|
|
case EBlendType::BT_BLEND: check(false); break;
|
|
|
|
|
default: check(false);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
switch (EBlendType(Args.blendType))
|
|
|
|
|
{
|
2023-05-24 02:50:03 -04:00
|
|
|
case EBlendType::BT_NORMAL_COMBINE: ImageNormalCombine(Result.get(), Result.get(), Color); break;
|
|
|
|
|
case EBlendType::BT_SOFTLIGHT: BufferLayerColour<SoftLightChannel>(Result.get(), Result.get(), Color); break;
|
|
|
|
|
case EBlendType::BT_HARDLIGHT: BufferLayerColour<HardLightChannel>(Result.get(), Result.get(), Color); break;
|
|
|
|
|
case EBlendType::BT_BURN: BufferLayerColour<BurnChannel>(Result.get(), Result.get(), Color); break;
|
|
|
|
|
case EBlendType::BT_DODGE: BufferLayerColour<DodgeChannel>(Result.get(), Result.get(), Color); break;
|
|
|
|
|
case EBlendType::BT_SCREEN: BufferLayerColour<ScreenChannel>(Result.get(), Result.get(), Color); break;
|
|
|
|
|
case EBlendType::BT_OVERLAY: BufferLayerColour<OverlayChannel>(Result.get(), Result.get(), Color); break;
|
|
|
|
|
case EBlendType::BT_LIGHTEN: BufferLayerColour<LightenChannel>(Result.get(), Result.get(), Color); break;
|
|
|
|
|
case EBlendType::BT_MULTIPLY: BufferLayerColour<MultiplyChannel>(Result.get(), Result.get(), Color); break;
|
2023-06-23 02:01:13 -04:00
|
|
|
case EBlendType::BT_BLEND:
|
|
|
|
|
{
|
|
|
|
|
// In this case we know it is already an uncompressed image, and we won't need additional allocations;
|
2023-09-13 06:59:56 -04:00
|
|
|
FImageOperator ImOp = FImageOperator::GetDefault(FImageOperator::FImagePixelFormatFunc());
|
2023-06-23 02:01:13 -04:00
|
|
|
ImOp.FillColor( Result.get(), Color);
|
|
|
|
|
break;
|
|
|
|
|
}
|
2023-01-11 14:28:53 -05:00
|
|
|
default: check(false);
|
|
|
|
|
}
|
2022-11-21 09:44:42 -05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Does it apply to alpha?
|
|
|
|
|
if (EBlendType(Args.blendTypeAlpha) != EBlendType::BT_NONE)
|
|
|
|
|
{
|
2023-05-24 02:50:03 -04:00
|
|
|
if (Args.mask && Mask)
|
2022-11-21 09:44:42 -05:00
|
|
|
{
|
|
|
|
|
// \todo: precalculated tables for softlight
|
|
|
|
|
switch (EBlendType(Args.blendTypeAlpha))
|
|
|
|
|
{
|
|
|
|
|
case EBlendType::BT_NORMAL_COMBINE: check(false); break;
|
2023-05-24 02:50:03 -04:00
|
|
|
case EBlendType::BT_SOFTLIGHT: BufferLayerColourInPlace<SoftLightChannelMasked, SoftLightChannel, 1>(Result.get(), Mask.get(), Color, bOnlyOneMip, 3, Args.BlendAlphaSourceChannel); break;
|
|
|
|
|
case EBlendType::BT_HARDLIGHT: BufferLayerColourInPlace<HardLightChannelMasked, HardLightChannel, 1>(Result.get(), Mask.get(), Color, bOnlyOneMip, 3, Args.BlendAlphaSourceChannel); break;
|
|
|
|
|
case EBlendType::BT_BURN: BufferLayerColourInPlace<BurnChannelMasked, BurnChannel, 1>(Result.get(), Mask.get(), Color, bOnlyOneMip, 3, Args.BlendAlphaSourceChannel); break;
|
|
|
|
|
case EBlendType::BT_DODGE: BufferLayerColourInPlace<DodgeChannelMasked, DodgeChannel, 1>(Result.get(), Mask.get(), Color, bOnlyOneMip, 3, Args.BlendAlphaSourceChannel); break;
|
|
|
|
|
case EBlendType::BT_SCREEN: BufferLayerColourInPlace<ScreenChannelMasked, ScreenChannel, 1>(Result.get(), Mask.get(), Color, bOnlyOneMip, 3, Args.BlendAlphaSourceChannel); break;
|
|
|
|
|
case EBlendType::BT_OVERLAY: BufferLayerColourInPlace<OverlayChannelMasked, OverlayChannel, 1>(Result.get(), Mask.get(), Color, bOnlyOneMip, 3, Args.BlendAlphaSourceChannel); break;
|
|
|
|
|
case EBlendType::BT_LIGHTEN: BufferLayerColourInPlace<LightenChannelMasked, LightenChannel, 1>(Result.get(), Mask.get(), Color, bOnlyOneMip, 3, Args.BlendAlphaSourceChannel); break;
|
|
|
|
|
case EBlendType::BT_MULTIPLY: BufferLayerColourInPlace<MultiplyChannelMasked, MultiplyChannel, 1>(Result.get(), Mask.get(), Color, bOnlyOneMip, 3, Args.BlendAlphaSourceChannel); break;
|
|
|
|
|
case EBlendType::BT_BLEND: BufferLayerColourInPlace<BlendChannelMasked, BlendChannel, 1>(Result.get(), Mask.get(), Color, bOnlyOneMip, 3, Args.BlendAlphaSourceChannel); break;
|
2022-11-21 09:44:42 -05:00
|
|
|
default: check(false);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
switch (EBlendType(Args.blendTypeAlpha))
|
|
|
|
|
{
|
|
|
|
|
case EBlendType::BT_NORMAL_COMBINE: check(false); break;
|
2023-05-24 02:50:03 -04:00
|
|
|
case EBlendType::BT_SOFTLIGHT: BufferLayerColourInPlace<SoftLightChannel, 1>(Result.get(), Color, bOnlyOneMip, 3, Args.BlendAlphaSourceChannel); break;
|
|
|
|
|
case EBlendType::BT_HARDLIGHT: BufferLayerColourInPlace<HardLightChannel, 1>(Result.get(), Color, bOnlyOneMip, 3, Args.BlendAlphaSourceChannel); break;
|
|
|
|
|
case EBlendType::BT_BURN: BufferLayerColourInPlace<BurnChannel, 1>(Result.get(), Color, bOnlyOneMip, 3, Args.BlendAlphaSourceChannel); break;
|
|
|
|
|
case EBlendType::BT_DODGE: BufferLayerColourInPlace<DodgeChannel, 1>(Result.get(), Color, bOnlyOneMip, 3, Args.BlendAlphaSourceChannel); break;
|
|
|
|
|
case EBlendType::BT_SCREEN: BufferLayerColourInPlace<ScreenChannel, 1>(Result.get(), Color, bOnlyOneMip, 3, Args.BlendAlphaSourceChannel); break;
|
|
|
|
|
case EBlendType::BT_OVERLAY: BufferLayerColourInPlace<OverlayChannel, 1>(Result.get(), Color, bOnlyOneMip, 3, Args.BlendAlphaSourceChannel); break;
|
|
|
|
|
case EBlendType::BT_LIGHTEN: BufferLayerColourInPlace<LightenChannel, 1>(Result.get(), Color, bOnlyOneMip, 3, Args.BlendAlphaSourceChannel); break;
|
|
|
|
|
case EBlendType::BT_MULTIPLY: BufferLayerColourInPlace<MultiplyChannel, 1>(Result.get(), Color, bOnlyOneMip, 3, Args.BlendAlphaSourceChannel); break;
|
|
|
|
|
case EBlendType::BT_BLEND: BufferLayerColourInPlace<BlendChannel, 1>(Result.get(), Color, bOnlyOneMip, 3, Args.BlendAlphaSourceChannel); break;
|
2022-11-21 09:44:42 -05:00
|
|
|
default: check(false);
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-09-26 15:12:13 -04:00
|
|
|
}
|
|
|
|
|
|
2023-02-08 08:22:43 -05:00
|
|
|
// Reset relevancy map.
|
2023-05-24 02:50:03 -04:00
|
|
|
Result->m_flags &= ~Image::EImageFlags::IF_HAS_RELEVANCY_MAP;
|
2022-09-26 15:12:13 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2024-06-14 04:01:28 -04:00
|
|
|
bool FImageLayerColourTask::Complete(CodeRunner* Runner)
|
2022-09-26 15:12:13 -04:00
|
|
|
{
|
2023-05-24 02:50:03 -04:00
|
|
|
// This runs in the Runner thread
|
|
|
|
|
Runner->Release(Mask);
|
|
|
|
|
|
|
|
|
|
// If no shortcut was taken
|
|
|
|
|
if (Result)
|
|
|
|
|
{
|
|
|
|
|
Runner->StoreImage(Op, Result);
|
|
|
|
|
}
|
2024-06-14 04:01:28 -04:00
|
|
|
|
|
|
|
|
bool bSuccess = true;
|
|
|
|
|
return bSuccess;
|
2022-09-26 15:12:13 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//---------------------------------------------------------------------------------------------
|
|
|
|
|
//---------------------------------------------------------------------------------------------
|
|
|
|
|
//---------------------------------------------------------------------------------------------
|
|
|
|
|
class FImagePixelFormatTask : public CodeRunner::FIssuedTask
|
|
|
|
|
{
|
|
|
|
|
public:
|
2023-05-24 02:50:03 -04:00
|
|
|
FImagePixelFormatTask(const FScheduledOp& InOp, const OP::ImagePixelFormatArgs& InArgs)
|
|
|
|
|
: FIssuedTask(InOp), Args(InArgs)
|
|
|
|
|
{}
|
2022-09-26 15:12:13 -04:00
|
|
|
|
|
|
|
|
// FIssuedTask interface
|
2024-06-14 04:01:28 -04:00
|
|
|
virtual bool Prepare(CodeRunner*, bool& bOutFailed) override;
|
|
|
|
|
virtual void DoWork() override;
|
|
|
|
|
virtual bool Complete(CodeRunner*) override;
|
2022-09-26 15:12:13 -04:00
|
|
|
|
|
|
|
|
private:
|
2023-05-24 02:50:03 -04:00
|
|
|
int ImageCompressionQuality = 0;
|
2022-09-26 15:12:13 -04:00
|
|
|
OP::ImagePixelFormatArgs Args;
|
2023-05-24 02:50:03 -04:00
|
|
|
EImageFormat TargetFormat = EImageFormat::IF_NONE;
|
|
|
|
|
Ptr<const Image> Base;
|
|
|
|
|
Ptr<Image> Result;
|
2023-09-13 06:59:56 -04:00
|
|
|
FImageOperator::FImagePixelFormatFunc ImagePixelFormatFunc;
|
2022-09-26 15:12:13 -04:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
2023-05-24 02:50:03 -04:00
|
|
|
bool FImagePixelFormatTask::Prepare(CodeRunner* Runner, bool& bOutFailed)
|
2022-09-26 15:12:13 -04:00
|
|
|
{
|
2023-05-24 02:50:03 -04:00
|
|
|
// This runs in the mutable Runner thread
|
2023-10-03 03:50:44 -04:00
|
|
|
MUTABLE_CPUPROFILER_SCOPE(FImageLayerPixelFormatTask_Prepare);
|
2023-05-24 02:50:03 -04:00
|
|
|
bOutFailed = false;
|
2022-09-26 15:12:13 -04:00
|
|
|
|
2023-05-24 02:50:03 -04:00
|
|
|
ImageCompressionQuality = Runner->m_pSettings->ImageCompressionQuality;
|
2022-09-26 15:12:13 -04:00
|
|
|
|
2023-05-24 02:50:03 -04:00
|
|
|
Base = Runner->LoadImage({ Args.source, Op.ExecutionIndex, Op.ExecutionOptions });
|
|
|
|
|
check(!Base || Base->GetFormat() < EImageFormat::IF_COUNT);
|
2022-09-26 15:12:13 -04:00
|
|
|
|
2023-05-24 02:50:03 -04:00
|
|
|
// Shortcuts
|
|
|
|
|
if (!Base)
|
|
|
|
|
{
|
|
|
|
|
Runner->StoreImage(Op, Base);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2022-09-26 15:12:13 -04:00
|
|
|
|
2023-05-24 02:50:03 -04:00
|
|
|
bool bValid = Base->GetSizeX() > 0 && Base->GetSizeY() > 0;
|
|
|
|
|
if (!bValid)
|
|
|
|
|
{
|
|
|
|
|
Runner->StoreImage(Op, Base);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2022-09-26 15:12:13 -04:00
|
|
|
|
2023-05-24 02:50:03 -04:00
|
|
|
TargetFormat = Args.format;
|
2022-09-26 15:12:13 -04:00
|
|
|
if (Args.formatIfAlpha != EImageFormat::IF_NONE
|
|
|
|
|
&&
|
2023-06-23 02:01:13 -04:00
|
|
|
GetImageFormatData(Base->GetFormat()).Channels > 3)
|
2022-09-26 15:12:13 -04:00
|
|
|
{
|
2023-05-24 02:50:03 -04:00
|
|
|
TargetFormat = Args.formatIfAlpha;
|
2022-09-26 15:12:13 -04:00
|
|
|
}
|
|
|
|
|
|
2023-05-24 02:50:03 -04:00
|
|
|
if (TargetFormat == EImageFormat::IF_NONE || TargetFormat == Base->GetFormat())
|
2022-09-26 15:12:13 -04:00
|
|
|
{
|
2023-05-24 02:50:03 -04:00
|
|
|
Runner->StoreImage(Op, Base);
|
|
|
|
|
return false;
|
2022-09-26 15:12:13 -04:00
|
|
|
}
|
|
|
|
|
|
2023-09-13 06:59:56 -04:00
|
|
|
ImagePixelFormatFunc = Runner->m_pSystem->ImagePixelFormatOverride;
|
|
|
|
|
|
2023-05-24 02:50:03 -04:00
|
|
|
// Create destination data
|
|
|
|
|
Result = Runner->CreateImage(Base->GetSizeX(), Base->GetSizeY(), Base->GetLODCount(), TargetFormat, EInitializationType::NotInitialized);
|
|
|
|
|
return true;
|
2022-09-26 15:12:13 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2023-05-24 02:50:03 -04:00
|
|
|
void FImagePixelFormatTask::DoWork()
|
2022-09-26 15:12:13 -04:00
|
|
|
{
|
2023-05-24 02:50:03 -04:00
|
|
|
// This runs in a worker thread
|
|
|
|
|
MUTABLE_CPUPROFILER_SCOPE(FImagePixelFormatTask);
|
|
|
|
|
|
|
|
|
|
bool bSuccess = false;
|
2023-09-13 06:59:56 -04:00
|
|
|
FImageOperator ImOp = FImageOperator::GetDefault(ImagePixelFormatFunc);
|
2023-06-23 02:01:13 -04:00
|
|
|
ImOp.ImagePixelFormat(bSuccess, ImageCompressionQuality, Result.get(), Base.get(), -1);
|
2024-03-12 07:17:49 -04:00
|
|
|
check(bSuccess);
|
2023-05-24 02:50:03 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2024-06-14 04:01:28 -04:00
|
|
|
bool FImagePixelFormatTask::Complete(CodeRunner* Runner)
|
2023-05-24 02:50:03 -04:00
|
|
|
{
|
|
|
|
|
// This runs in the Runner thread
|
|
|
|
|
Runner->Release(Base);
|
|
|
|
|
|
|
|
|
|
// If no shortcut was taken
|
|
|
|
|
if (Result)
|
|
|
|
|
{
|
|
|
|
|
Runner->StoreImage(Op, Result);
|
|
|
|
|
}
|
2024-06-14 04:01:28 -04:00
|
|
|
|
|
|
|
|
bool bSuccess = true;
|
|
|
|
|
return bSuccess;
|
2022-09-26 15:12:13 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
class FImageMipmapTask : public CodeRunner::FIssuedTask
|
|
|
|
|
{
|
|
|
|
|
public:
|
2023-05-24 02:50:03 -04:00
|
|
|
FImageMipmapTask(const FScheduledOp& InOp, const OP::ImageMipmapArgs& InArgs)
|
|
|
|
|
:FIssuedTask(InOp), Args(InArgs)
|
|
|
|
|
{}
|
2022-09-26 15:12:13 -04:00
|
|
|
|
|
|
|
|
// FIssuedTask interface
|
2024-06-14 04:01:28 -04:00
|
|
|
virtual bool Prepare(CodeRunner*, bool& bOutFailed) override;
|
|
|
|
|
virtual void DoWork() override;
|
|
|
|
|
virtual bool Complete(CodeRunner*) override;
|
2022-09-26 15:12:13 -04:00
|
|
|
|
|
|
|
|
private:
|
2024-03-12 07:17:49 -04:00
|
|
|
int32 ImageCompressionQuality = 0;
|
|
|
|
|
int32 StartLevel = -1;
|
2022-09-26 15:12:13 -04:00
|
|
|
OP::ImageMipmapArgs Args;
|
2023-05-24 02:50:03 -04:00
|
|
|
Ptr<const Image> Base;
|
|
|
|
|
Ptr<Image> Result;
|
2023-06-23 02:01:13 -04:00
|
|
|
FImageOperator::FScratchImageMipmap Scratch;
|
2023-09-13 06:59:56 -04:00
|
|
|
FImageOperator::FImagePixelFormatFunc ImagePixelFormatFunc;
|
2022-09-26 15:12:13 -04:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
2023-05-24 02:50:03 -04:00
|
|
|
bool FImageMipmapTask::Prepare(CodeRunner* Runner, bool& bOutFailed)
|
2022-09-26 15:12:13 -04:00
|
|
|
{
|
2023-05-24 02:50:03 -04:00
|
|
|
// This runs in the mutable Runner thread
|
2023-10-03 03:50:44 -04:00
|
|
|
MUTABLE_CPUPROFILER_SCOPE(FImageMipmapTask_Prepare);
|
2023-05-24 02:50:03 -04:00
|
|
|
bOutFailed = false;
|
|
|
|
|
|
|
|
|
|
ImageCompressionQuality = Runner->m_pSettings->ImageCompressionQuality;
|
|
|
|
|
|
|
|
|
|
Base = Runner->LoadImage({ Args.source, Op.ExecutionIndex, Op.ExecutionOptions });
|
|
|
|
|
check(!Base || Base->GetFormat() < EImageFormat::IF_COUNT);
|
|
|
|
|
|
|
|
|
|
// Shortcuts
|
|
|
|
|
if (!Base)
|
|
|
|
|
{
|
|
|
|
|
Runner->StoreImage(Op, Base);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool bValid = Base->GetSizeX() > 0 && Base->GetSizeY() > 0;
|
|
|
|
|
if (!bValid)
|
|
|
|
|
{
|
|
|
|
|
Runner->StoreImage(Op, Base);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int32 LevelCount = Args.levels;
|
|
|
|
|
int32 MaxLevelCount = Image::GetMipmapCount(Base->GetSizeX(), Base->GetSizeY());
|
|
|
|
|
if (LevelCount == 0)
|
|
|
|
|
{
|
|
|
|
|
LevelCount = MaxLevelCount;
|
|
|
|
|
}
|
|
|
|
|
else if (LevelCount > MaxLevelCount)
|
|
|
|
|
{
|
|
|
|
|
// If code generation is smart enough, this should never happen.
|
|
|
|
|
// \todo But apparently it does, sometimes.
|
|
|
|
|
LevelCount = MaxLevelCount;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// At least keep the levels we already have.
|
2024-03-12 07:17:49 -04:00
|
|
|
LevelCount = FMath::Max(Base->GetLODCount(), LevelCount);
|
2023-05-31 02:39:45 -04:00
|
|
|
|
|
|
|
|
if (LevelCount == Base->GetLODCount())
|
|
|
|
|
{
|
|
|
|
|
Runner->StoreImage(Op, Base);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2023-05-24 02:50:03 -04:00
|
|
|
|
2024-03-12 07:17:49 -04:00
|
|
|
StartLevel = Base->GetLODCount() - 1;
|
|
|
|
|
|
|
|
|
|
Result = Runner->CloneOrTakeOver(Base);
|
|
|
|
|
Base = nullptr;
|
|
|
|
|
|
|
|
|
|
Result->DataStorage.SetNumLODs(LevelCount);
|
2023-05-24 02:50:03 -04:00
|
|
|
|
2023-06-23 02:01:13 -04:00
|
|
|
FImageOperator ImOp = MakeImageOperator(Runner);
|
2024-03-12 07:17:49 -04:00
|
|
|
ImOp.ImageMipmap_PrepareScratch(Result.get(), StartLevel, LevelCount, Scratch);
|
2023-05-24 02:50:03 -04:00
|
|
|
|
2023-09-13 06:59:56 -04:00
|
|
|
ImagePixelFormatFunc = Runner->m_pSystem->ImagePixelFormatOverride;
|
|
|
|
|
|
2023-05-24 02:50:03 -04:00
|
|
|
return true;
|
2022-09-26 15:12:13 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void FImageMipmapTask::DoWork()
|
|
|
|
|
{
|
2023-05-24 02:50:03 -04:00
|
|
|
// This runs in a worker thread
|
2022-09-26 15:12:13 -04:00
|
|
|
MUTABLE_CPUPROFILER_SCOPE(FImageMipmapTask);
|
|
|
|
|
|
2024-03-12 07:17:49 -04:00
|
|
|
check(StartLevel >= 0);
|
|
|
|
|
|
2024-03-26 07:54:30 -04:00
|
|
|
FMipmapGenerationSettings Settings{Args.FilterType, Args.AddressMode};
|
2023-09-13 06:59:56 -04:00
|
|
|
FImageOperator ImOp = FImageOperator::GetDefault(ImagePixelFormatFunc);
|
2024-03-12 07:17:49 -04:00
|
|
|
ImOp.ImageMipmap(Scratch, ImageCompressionQuality, Result.get(), Result.get(), StartLevel, Result->GetLODCount(), Settings);
|
2022-09-26 15:12:13 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2024-06-14 04:01:28 -04:00
|
|
|
bool FImageMipmapTask::Complete(CodeRunner* Runner)
|
2022-09-26 15:12:13 -04:00
|
|
|
{
|
2023-06-23 02:01:13 -04:00
|
|
|
FImageOperator ImOp = MakeImageOperator(Runner);
|
|
|
|
|
ImOp.ImageMipmap_ReleaseScratch(Scratch);
|
2023-05-24 02:50:03 -04:00
|
|
|
|
|
|
|
|
// This runs in the Runner thread
|
2024-03-12 07:17:49 -04:00
|
|
|
if (Base)
|
|
|
|
|
{
|
|
|
|
|
Runner->Release(Base);
|
|
|
|
|
}
|
2023-05-24 02:50:03 -04:00
|
|
|
|
|
|
|
|
// If no shortcut was taken
|
|
|
|
|
if (Result)
|
|
|
|
|
{
|
|
|
|
|
Runner->StoreImage(Op, Result);
|
|
|
|
|
}
|
2024-06-14 04:01:28 -04:00
|
|
|
|
|
|
|
|
bool bSuccess = true;
|
|
|
|
|
return bSuccess;
|
2022-09-26 15:12:13 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//---------------------------------------------------------------------------------------------
|
|
|
|
|
//---------------------------------------------------------------------------------------------
|
|
|
|
|
//---------------------------------------------------------------------------------------------
|
|
|
|
|
class FImageSwizzleTask : public CodeRunner::FIssuedTask
|
|
|
|
|
{
|
|
|
|
|
public:
|
2023-05-24 02:50:03 -04:00
|
|
|
FImageSwizzleTask(const FScheduledOp& InOp, const OP::ImageSwizzleArgs& InArgs)
|
|
|
|
|
: FIssuedTask(InOp), Args(InArgs)
|
|
|
|
|
{}
|
2022-09-26 15:12:13 -04:00
|
|
|
|
|
|
|
|
// FIssuedTask interface
|
2024-06-14 04:01:28 -04:00
|
|
|
virtual bool Prepare(CodeRunner*, bool& bOutFailed);
|
|
|
|
|
virtual void DoWork() override;
|
|
|
|
|
virtual bool Complete(CodeRunner*) override;
|
2022-09-26 15:12:13 -04:00
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
OP::ImageSwizzleArgs Args;
|
|
|
|
|
Ptr<const Image> Sources[MUTABLE_OP_MAX_SWIZZLE_CHANNELS];
|
2023-05-24 02:50:03 -04:00
|
|
|
Ptr<Image> Result;
|
2022-09-26 15:12:13 -04:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
2023-05-24 02:50:03 -04:00
|
|
|
bool FImageSwizzleTask::Prepare(CodeRunner* Runner, bool& bOutFailed)
|
2022-09-26 15:12:13 -04:00
|
|
|
{
|
2023-05-24 02:50:03 -04:00
|
|
|
// This runs in the mutable Runner thread
|
2023-08-25 05:26:54 -04:00
|
|
|
MUTABLE_CPUPROFILER_SCOPE(FImageSwizzleTask_Prepare);
|
2023-05-24 02:50:03 -04:00
|
|
|
bOutFailed = false;
|
|
|
|
|
|
2024-09-04 05:37:04 -04:00
|
|
|
int32 FirstValidSourceIndex = -1;
|
|
|
|
|
for (int32 SourceIndex = 0; SourceIndex < MUTABLE_OP_MAX_SWIZZLE_CHANNELS; ++SourceIndex)
|
2022-09-26 15:12:13 -04:00
|
|
|
{
|
2024-09-04 05:37:04 -04:00
|
|
|
if (Args.sources[SourceIndex])
|
2022-09-26 15:12:13 -04:00
|
|
|
{
|
2024-09-04 05:37:04 -04:00
|
|
|
FirstValidSourceIndex = SourceIndex;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (int32 SourceIndex = 0; SourceIndex < MUTABLE_OP_MAX_SWIZZLE_CHANNELS; ++SourceIndex)
|
|
|
|
|
{
|
|
|
|
|
if (Args.sources[SourceIndex])
|
|
|
|
|
{
|
|
|
|
|
Sources[SourceIndex] = Runner->LoadImage({ Args.sources[SourceIndex], Op.ExecutionIndex, Op.ExecutionOptions });
|
2022-09-26 15:12:13 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-05-24 02:50:03 -04:00
|
|
|
// Shortcuts
|
2024-09-04 05:37:04 -04:00
|
|
|
if (FirstValidSourceIndex < 0 || !Sources[FirstValidSourceIndex])
|
2023-05-24 02:50:03 -04:00
|
|
|
{
|
2024-09-04 05:37:04 -04:00
|
|
|
for (int32 SourceIndex = 0; SourceIndex < MUTABLE_OP_MAX_SWIZZLE_CHANNELS; ++SourceIndex)
|
2023-05-24 02:50:03 -04:00
|
|
|
{
|
2024-09-04 05:37:04 -04:00
|
|
|
Runner->Release(Sources[SourceIndex]);
|
2023-05-24 02:50:03 -04:00
|
|
|
}
|
|
|
|
|
Runner->StoreImage(Op, nullptr);
|
2024-09-04 05:37:04 -04:00
|
|
|
|
2023-05-24 02:50:03 -04:00
|
|
|
return false;
|
|
|
|
|
}
|
2022-09-26 15:12:13 -04:00
|
|
|
|
2023-05-24 02:50:03 -04:00
|
|
|
// Create destination data
|
|
|
|
|
EImageFormat format = (EImageFormat)Args.format;
|
2022-09-26 15:12:13 -04:00
|
|
|
|
2023-06-23 02:01:13 -04:00
|
|
|
FImageOperator ImOp = MakeImageOperator(Runner);
|
|
|
|
|
|
2024-09-04 05:37:04 -04:00
|
|
|
int32 ResultLODs = Sources[FirstValidSourceIndex]->GetLODCount();
|
2023-06-23 02:01:13 -04:00
|
|
|
|
2023-12-19 05:19:14 -05:00
|
|
|
// Be defensive: ensure formats are uncompressed
|
2024-09-04 05:37:04 -04:00
|
|
|
for (int32 SourceIndex = 0; SourceIndex < MUTABLE_OP_MAX_SWIZZLE_CHANNELS; ++SourceIndex)
|
2023-12-19 05:19:14 -05:00
|
|
|
{
|
2024-09-04 05:37:04 -04:00
|
|
|
if (Sources[SourceIndex] && Sources[SourceIndex]->GetFormat() != GetUncompressedFormat(Sources[SourceIndex]->GetFormat()))
|
2023-12-19 05:19:14 -05:00
|
|
|
{
|
|
|
|
|
MUTABLE_CPUPROFILER_SCOPE(ImageFormat_ForSwizzle);
|
|
|
|
|
|
2024-09-04 05:37:04 -04:00
|
|
|
EImageFormat UncompressedFormat = GetUncompressedFormat(Sources[SourceIndex]->GetFormat());
|
|
|
|
|
Ptr<Image> Formatted = Runner->CreateImage(Sources[SourceIndex]->GetSizeX(), Sources[SourceIndex]->GetSizeY(), 1, UncompressedFormat, EInitializationType::NotInitialized);
|
2023-12-19 05:19:14 -05:00
|
|
|
bool bSuccess = false;
|
|
|
|
|
int32 ImageCompressionQuality = 4; // TODO
|
2024-09-04 05:37:04 -04:00
|
|
|
ImOp.ImagePixelFormat(bSuccess, ImageCompressionQuality, Formatted.get(), Sources[SourceIndex].get());
|
2023-12-19 05:19:14 -05:00
|
|
|
check(bSuccess); // Decompression cannot fail
|
2024-09-04 05:37:04 -04:00
|
|
|
Runner->Release(Sources[SourceIndex]);
|
|
|
|
|
Sources[SourceIndex] = Formatted;
|
2023-12-19 05:19:14 -05:00
|
|
|
ResultLODs = 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-09-04 05:37:04 -04:00
|
|
|
const FImageSize ResultSize = Sources[FirstValidSourceIndex]->GetSize();
|
2023-12-19 05:19:14 -05:00
|
|
|
|
2022-09-26 15:12:13 -04:00
|
|
|
// Be defensive: ensure image sizes match.
|
2024-09-04 05:37:04 -04:00
|
|
|
for (int32 SourceIndex = FirstValidSourceIndex + 1; SourceIndex < MUTABLE_OP_MAX_SWIZZLE_CHANNELS; ++SourceIndex)
|
2022-09-26 15:12:13 -04:00
|
|
|
{
|
2024-09-04 05:37:04 -04:00
|
|
|
if (Sources[SourceIndex] && ResultSize != Sources[SourceIndex]->GetSize())
|
2022-09-26 15:12:13 -04:00
|
|
|
{
|
|
|
|
|
MUTABLE_CPUPROFILER_SCOPE(ImageResize_ForSwizzle);
|
2024-09-04 05:37:04 -04:00
|
|
|
Ptr<Image> Resized = Runner->CreateImage(ResultSize.X, ResultSize.Y, 1, Sources[SourceIndex]->GetFormat(), EInitializationType::NotInitialized);
|
|
|
|
|
ImOp.ImageResizeLinear(Resized.get(), 0, Sources[SourceIndex].get());
|
|
|
|
|
Runner->Release(Sources[SourceIndex]);
|
|
|
|
|
Sources[SourceIndex] = Resized;
|
2023-06-23 02:01:13 -04:00
|
|
|
ResultLODs = 1;
|
2022-09-26 15:12:13 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-14 04:05:45 -05:00
|
|
|
// If any source has only 1 LOD, then the result has to have 1 LOD and the rest be regenerated later on
|
|
|
|
|
for (int32 SourceIndex = 0; SourceIndex < MUTABLE_OP_MAX_SWIZZLE_CHANNELS; ++SourceIndex)
|
|
|
|
|
{
|
|
|
|
|
if (Sources[SourceIndex] && Sources[SourceIndex]->GetLODCount() == 1)
|
|
|
|
|
{
|
|
|
|
|
ResultLODs = 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-09-04 05:37:04 -04:00
|
|
|
Result = Runner->CreateImage(ResultSize.X, ResultSize.Y, ResultLODs, Args.format, EInitializationType::Black);
|
2023-05-24 02:50:03 -04:00
|
|
|
return true;
|
2022-09-26 15:12:13 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2023-05-24 02:50:03 -04:00
|
|
|
void FImageSwizzleTask::DoWork()
|
2022-09-26 15:12:13 -04:00
|
|
|
{
|
2023-05-24 02:50:03 -04:00
|
|
|
// This runs in a worker thread
|
|
|
|
|
MUTABLE_CPUPROFILER_SCOPE(FImageSwizzleTask);
|
|
|
|
|
|
|
|
|
|
ImageSwizzle(Result.get(), Sources, Args.sourceChannels);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2024-06-14 04:01:28 -04:00
|
|
|
bool FImageSwizzleTask::Complete(CodeRunner* Runner)
|
2023-05-24 02:50:03 -04:00
|
|
|
{
|
|
|
|
|
// This runs in the Runner thread
|
2024-09-04 05:37:04 -04:00
|
|
|
for (int32 SourceIndex = 0; SourceIndex < MUTABLE_OP_MAX_SWIZZLE_CHANNELS; ++SourceIndex)
|
2023-05-24 02:50:03 -04:00
|
|
|
{
|
2024-09-04 05:37:04 -04:00
|
|
|
Runner->Release(Sources[SourceIndex]);
|
2023-05-24 02:50:03 -04:00
|
|
|
}
|
|
|
|
|
|
2023-06-23 02:01:13 -04:00
|
|
|
// \TODO: If Result LODs differ from Source[0]'s, rebuild mips?
|
|
|
|
|
|
2023-05-24 02:50:03 -04:00
|
|
|
// If no shortcut was taken
|
|
|
|
|
if (Result)
|
|
|
|
|
{
|
|
|
|
|
Runner->StoreImage(Op, Result);
|
|
|
|
|
}
|
2024-06-14 04:01:28 -04:00
|
|
|
|
|
|
|
|
bool bSuccess = true;
|
|
|
|
|
return bSuccess;
|
2022-09-26 15:12:13 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2023-01-26 05:27:42 -05:00
|
|
|
//---------------------------------------------------------------------------------------------
|
|
|
|
|
//---------------------------------------------------------------------------------------------
|
|
|
|
|
//---------------------------------------------------------------------------------------------
|
|
|
|
|
class FImageSaturateTask : public CodeRunner::FIssuedTask
|
|
|
|
|
{
|
|
|
|
|
public:
|
2023-05-24 02:50:03 -04:00
|
|
|
FImageSaturateTask(const FScheduledOp&, const OP::ImageSaturateArgs&);
|
2023-01-26 05:27:42 -05:00
|
|
|
|
|
|
|
|
// FIssuedTask interface
|
2024-06-14 04:01:28 -04:00
|
|
|
virtual bool Prepare(CodeRunner*, bool& bOutFailed) override;
|
|
|
|
|
virtual void DoWork() override;
|
|
|
|
|
virtual bool Complete(CodeRunner*) override;
|
2023-01-26 05:27:42 -05:00
|
|
|
|
|
|
|
|
private:
|
2023-05-24 02:50:03 -04:00
|
|
|
const OP::ImageSaturateArgs Args;
|
|
|
|
|
Ptr<Image> Result;
|
2023-01-26 05:27:42 -05:00
|
|
|
float Factor;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//---------------------------------------------------------------------------------------------
|
2023-05-24 02:50:03 -04:00
|
|
|
FImageSaturateTask::FImageSaturateTask(const FScheduledOp& InOp, const OP::ImageSaturateArgs& InArgs)
|
2023-02-14 12:12:46 -05:00
|
|
|
: FIssuedTask(InOp)
|
2023-05-24 02:50:03 -04:00
|
|
|
, Args(InArgs)
|
2023-01-26 05:27:42 -05:00
|
|
|
{
|
2023-05-24 02:50:03 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//---------------------------------------------------------------------------------------------
|
|
|
|
|
bool FImageSaturateTask::Prepare(CodeRunner* Runner, bool& bOutFailed)
|
|
|
|
|
{
|
|
|
|
|
// This runs in the mutable Runner thread
|
|
|
|
|
MUTABLE_CPUPROFILER_SCOPE(FImageSaturateTask_Prepare);
|
|
|
|
|
bOutFailed = false;
|
|
|
|
|
|
|
|
|
|
Ptr<const Image> Source = Runner->LoadImage(FCacheAddress(Args.base, Op));
|
|
|
|
|
Factor = Runner->LoadScalar(FScheduledOp::FromOpAndOptions(Args.factor, Op, 0));
|
|
|
|
|
|
|
|
|
|
if (!Source)
|
|
|
|
|
{
|
|
|
|
|
Runner->StoreImage(Op, Source);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const bool bOptimizeUnchanged = FMath::IsNearlyEqual(Factor, 1.0f);
|
|
|
|
|
if (bOptimizeUnchanged)
|
|
|
|
|
{
|
|
|
|
|
Runner->StoreImage(Op, Source);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Result = Runner->CloneOrTakeOver(Source);
|
|
|
|
|
return true;
|
2023-01-26 05:27:42 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//---------------------------------------------------------------------------------------------
|
|
|
|
|
void FImageSaturateTask::DoWork()
|
|
|
|
|
{
|
2023-05-24 02:50:03 -04:00
|
|
|
// This runs on a random worker thread
|
2023-01-26 05:27:42 -05:00
|
|
|
MUTABLE_CPUPROFILER_SCOPE(FImageSaturateTask);
|
|
|
|
|
|
2024-03-12 07:17:49 -04:00
|
|
|
constexpr bool bUseVectorIntrinsics = true;
|
|
|
|
|
ImageSaturate<bUseVectorIntrinsics>(Result.get(), Factor);
|
2023-01-26 05:27:42 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//---------------------------------------------------------------------------------------------
|
2024-06-14 04:01:28 -04:00
|
|
|
bool FImageSaturateTask::Complete(CodeRunner* Runner)
|
2023-01-26 05:27:42 -05:00
|
|
|
{
|
2023-05-24 02:50:03 -04:00
|
|
|
// This runs in the mutable Runner thread
|
|
|
|
|
|
|
|
|
|
// If we didn't take a shortcut
|
|
|
|
|
if (Result)
|
|
|
|
|
{
|
|
|
|
|
Runner->StoreImage(Op, Result);
|
|
|
|
|
}
|
2024-06-14 04:01:28 -04:00
|
|
|
|
|
|
|
|
bool bSuccess = true;
|
|
|
|
|
return bSuccess;
|
2023-01-26 05:27:42 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//---------------------------------------------------------------------------------------------
|
|
|
|
|
//---------------------------------------------------------------------------------------------
|
|
|
|
|
//---------------------------------------------------------------------------------------------
|
|
|
|
|
class FImageResizeTask : public CodeRunner::FIssuedTask
|
|
|
|
|
{
|
|
|
|
|
public:
|
2023-05-24 02:50:03 -04:00
|
|
|
FImageResizeTask(const FScheduledOp& InOp, const OP::ImageResizeArgs& InArgs)
|
|
|
|
|
: FIssuedTask(InOp), Args(InArgs)
|
|
|
|
|
{}
|
2023-01-26 05:27:42 -05:00
|
|
|
|
|
|
|
|
// FIssuedTask interface
|
2024-06-14 04:01:28 -04:00
|
|
|
virtual bool Prepare(CodeRunner* Runner, bool& bFailed) override;
|
|
|
|
|
virtual void DoWork() override;
|
|
|
|
|
virtual bool Complete(CodeRunner*) override;
|
2023-01-26 05:27:42 -05:00
|
|
|
|
|
|
|
|
private:
|
2023-05-24 02:50:03 -04:00
|
|
|
int32 ImageCompressionQuality = 0;
|
|
|
|
|
OP::ImageResizeArgs Args;
|
|
|
|
|
Ptr<const Image> Base;
|
|
|
|
|
Ptr<Image> Result;
|
2023-09-13 06:59:56 -04:00
|
|
|
FImageOperator::FImagePixelFormatFunc ImagePixelFormatFunc;
|
2023-01-26 05:27:42 -05:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
2023-05-24 02:50:03 -04:00
|
|
|
bool FImageResizeTask::Prepare(CodeRunner* Runner, bool& bOutFailed)
|
2023-01-26 05:27:42 -05:00
|
|
|
{
|
2023-05-24 02:50:03 -04:00
|
|
|
// This runs in the mutable Runner thread
|
|
|
|
|
MUTABLE_CPUPROFILER_SCOPE(FImageResizeTask_Prepare);
|
|
|
|
|
bOutFailed = false;
|
2023-01-26 05:27:42 -05:00
|
|
|
|
2023-05-24 02:50:03 -04:00
|
|
|
ImageCompressionQuality = Runner->m_pSettings->ImageCompressionQuality;
|
2023-01-26 05:27:42 -05:00
|
|
|
|
2023-05-24 02:50:03 -04:00
|
|
|
FImageSize destSize = FImageSize(Args.size[0], Args.size[1]);
|
2023-01-26 05:27:42 -05:00
|
|
|
|
2023-02-08 08:22:43 -05:00
|
|
|
// Apply the mips-to-skip to the dest size
|
|
|
|
|
int32 MipsToSkip = Op.ExecutionOptions;
|
|
|
|
|
destSize[0] = FMath::Max(destSize[0] >> MipsToSkip, 1);
|
|
|
|
|
destSize[1] = FMath::Max(destSize[1] >> MipsToSkip, 1);
|
2023-01-26 05:27:42 -05:00
|
|
|
|
2023-05-24 02:50:03 -04:00
|
|
|
Base = Runner->LoadImage(FCacheAddress(Args.source, Op));
|
|
|
|
|
if (!Base
|
|
|
|
|
||
|
|
|
|
|
( Base->GetSizeX()==destSize[0] && Base->GetSizeY()==destSize[1] )
|
|
|
|
|
)
|
2023-01-26 05:27:42 -05:00
|
|
|
{
|
2023-05-24 02:50:03 -04:00
|
|
|
Runner->StoreImage(Op, Base);
|
|
|
|
|
return false;
|
2023-01-26 05:27:42 -05:00
|
|
|
}
|
2023-02-09 16:18:52 -05:00
|
|
|
|
2023-05-24 02:50:03 -04:00
|
|
|
int32 Lods = 1;
|
|
|
|
|
|
|
|
|
|
// If the source image had mips, generate them as well for the resized image.
|
|
|
|
|
// This shouldn't happen often since it should be usually optimised during model compilation.
|
|
|
|
|
// The mipmap generation below is not very precise with the number of mips that are needed and
|
|
|
|
|
// will probably generate too many
|
|
|
|
|
bool bSourceHasMips = Base->GetLODCount() > 1;
|
|
|
|
|
if (bSourceHasMips)
|
|
|
|
|
{
|
|
|
|
|
Lods = Image::GetMipmapCount(destSize[0], destSize[1]);
|
|
|
|
|
}
|
|
|
|
|
|
2023-11-20 04:22:10 -05:00
|
|
|
if (Base->IsReference())
|
|
|
|
|
{
|
|
|
|
|
// We are trying to resize an external reference. This shouldn't happen, but be deffensive.
|
|
|
|
|
Runner->StoreImage(Op, Base);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2023-05-24 02:50:03 -04:00
|
|
|
Result = Runner->CreateImage( destSize[0], destSize[1], Lods, Base->GetFormat(), EInitializationType::NotInitialized );
|
|
|
|
|
|
2023-09-13 06:59:56 -04:00
|
|
|
ImagePixelFormatFunc = Runner->m_pSystem->ImagePixelFormatOverride;
|
|
|
|
|
|
2023-05-24 02:50:03 -04:00
|
|
|
return true;
|
2023-01-26 05:27:42 -05:00
|
|
|
}
|
|
|
|
|
|
2023-05-24 02:50:03 -04:00
|
|
|
|
|
|
|
|
//---------------------------------------------------------------------------------------------
|
|
|
|
|
void FImageResizeTask::DoWork()
|
|
|
|
|
{
|
|
|
|
|
// This runs on a random worker thread
|
|
|
|
|
MUTABLE_CPUPROFILER_SCOPE(FImageResizeTask);
|
|
|
|
|
|
|
|
|
|
FImageSize destSize = FImageSize( Args.size[0], Args.size[1]);
|
|
|
|
|
|
|
|
|
|
// Apply the mips-to-skip to the dest size
|
|
|
|
|
int32 MipsToSkip = Op.ExecutionOptions;
|
|
|
|
|
destSize[0] = FMath::Max(destSize[0] >> MipsToSkip, 1);
|
|
|
|
|
destSize[1] = FMath::Max(destSize[1] >> MipsToSkip, 1);
|
|
|
|
|
|
|
|
|
|
// Warning: This will actually allocate temp memory that may exceed the budget.
|
|
|
|
|
// \TODO: Fix it.
|
2023-09-13 06:59:56 -04:00
|
|
|
FImageOperator ImOp = FImageOperator::GetDefault(ImagePixelFormatFunc);
|
2023-06-23 02:01:13 -04:00
|
|
|
ImOp.ImageResizeLinear( Result.get(), ImageCompressionQuality, Base.get());
|
2023-05-24 02:50:03 -04:00
|
|
|
|
|
|
|
|
int32 LodCount = Result->GetLODCount();
|
|
|
|
|
if (LodCount>1)
|
|
|
|
|
{
|
|
|
|
|
FMipmapGenerationSettings mipSettings{};
|
|
|
|
|
ImageMipmapInPlace( ImageCompressionQuality, Result.get(), mipSettings );
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2023-01-26 05:27:42 -05:00
|
|
|
//---------------------------------------------------------------------------------------------
|
2024-06-14 04:01:28 -04:00
|
|
|
bool FImageResizeTask::Complete(CodeRunner* Runner)
|
2023-01-26 05:27:42 -05:00
|
|
|
{
|
2023-05-24 02:50:03 -04:00
|
|
|
// This runs in the Runner thread
|
|
|
|
|
Runner->Release(Base);
|
|
|
|
|
|
|
|
|
|
// If didn't take a shortcut and set it already
|
|
|
|
|
if (Result)
|
|
|
|
|
{
|
|
|
|
|
Runner->StoreImage(Op, Result);
|
|
|
|
|
}
|
2024-06-14 04:01:28 -04:00
|
|
|
|
|
|
|
|
bool bSuccess = true;
|
|
|
|
|
return bSuccess;
|
2023-01-26 05:27:42 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//---------------------------------------------------------------------------------------------
|
|
|
|
|
//---------------------------------------------------------------------------------------------
|
|
|
|
|
//---------------------------------------------------------------------------------------------
|
|
|
|
|
class FImageResizeRelTask : public CodeRunner::FIssuedTask
|
|
|
|
|
{
|
|
|
|
|
public:
|
2023-05-24 02:50:03 -04:00
|
|
|
FImageResizeRelTask(const FScheduledOp& InOp, const OP::ImageResizeRelArgs& InArgs)
|
|
|
|
|
: FIssuedTask(InOp), Args(InArgs)
|
|
|
|
|
{}
|
2023-01-26 05:27:42 -05:00
|
|
|
|
|
|
|
|
// FIssuedTask interface
|
2024-06-14 04:01:28 -04:00
|
|
|
virtual bool Prepare(CodeRunner* Runner, bool& bFailed) override;
|
|
|
|
|
virtual void DoWork() override;
|
|
|
|
|
virtual bool Complete(CodeRunner*) override;
|
2023-01-26 05:27:42 -05:00
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
OP::ImageResizeRelArgs Args;
|
2023-05-24 02:50:03 -04:00
|
|
|
int32 ImageCompressionQuality=0;
|
|
|
|
|
Ptr<const Image> Base;
|
|
|
|
|
Ptr<Image> Result;
|
|
|
|
|
FImageSize DestSize;
|
2023-09-13 06:59:56 -04:00
|
|
|
FImageOperator::FImagePixelFormatFunc ImagePixelFormatFunc;
|
2023-01-26 05:27:42 -05:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
2023-05-24 02:50:03 -04:00
|
|
|
bool FImageResizeRelTask::Prepare(CodeRunner* Runner, bool& bOutFailed)
|
2023-01-26 05:27:42 -05:00
|
|
|
{
|
2023-05-24 02:50:03 -04:00
|
|
|
// This runs in the mutable Runner thread
|
|
|
|
|
MUTABLE_CPUPROFILER_SCOPE(FImageResizeRelTask_Prepare);
|
|
|
|
|
bOutFailed = false;
|
2023-01-26 05:27:42 -05:00
|
|
|
|
2023-05-24 02:50:03 -04:00
|
|
|
ImageCompressionQuality = Runner->m_pSettings->ImageCompressionQuality;
|
2023-01-26 05:27:42 -05:00
|
|
|
|
2023-05-24 02:50:03 -04:00
|
|
|
Base = Runner->LoadImage(FCacheAddress(Args.source, Op));
|
|
|
|
|
if (!Base)
|
2023-01-26 05:27:42 -05:00
|
|
|
{
|
2023-05-24 02:50:03 -04:00
|
|
|
Runner->StoreImage(Op, Base);
|
|
|
|
|
return false;
|
2023-01-26 05:27:42 -05:00
|
|
|
}
|
|
|
|
|
|
2023-05-24 02:50:03 -04:00
|
|
|
DestSize = FImageSize(
|
|
|
|
|
uint16(FMath::Max(1.0, Base->GetSizeX() * Args.factor[0] + 0.5f)),
|
|
|
|
|
uint16(FMath::Max(1.0, Base->GetSizeY() * Args.factor[1] + 0.5f)));
|
2023-01-26 05:27:42 -05:00
|
|
|
|
2023-05-24 02:50:03 -04:00
|
|
|
if (Base->GetSizeX() == DestSize[0] && Base->GetSizeY() == DestSize[1])
|
|
|
|
|
{
|
|
|
|
|
Runner->StoreImage(Op, Base);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int32 Lods = 1;
|
2023-01-26 05:27:42 -05:00
|
|
|
|
|
|
|
|
// If the source image had mips, generate them as well for the resized image.
|
2023-05-24 02:50:03 -04:00
|
|
|
// This shouldn't happen often since it should be usually optimised during model compilation.
|
|
|
|
|
// The mipmap generation below is not very precise with the number of mips that are needed and
|
|
|
|
|
// will probably generate too many
|
|
|
|
|
bool bSourceHasMips = Base->GetLODCount() > 1;
|
|
|
|
|
if (bSourceHasMips)
|
2023-01-26 05:27:42 -05:00
|
|
|
{
|
2023-05-24 02:50:03 -04:00
|
|
|
Lods = Image::GetMipmapCount(DestSize[0], DestSize[1]);
|
2023-01-26 05:27:42 -05:00
|
|
|
}
|
|
|
|
|
|
2024-10-11 09:40:02 -04:00
|
|
|
if (Base->IsReference())
|
|
|
|
|
{
|
|
|
|
|
// We are trying to resize an external reference. This shouldn't happen, but be deffensive.
|
|
|
|
|
Runner->StoreImage(Op, Base);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2023-05-24 02:50:03 -04:00
|
|
|
Result = Runner->CreateImage(DestSize[0], DestSize[1], Lods, Base->GetFormat(), EInitializationType::NotInitialized);
|
|
|
|
|
|
2023-09-13 06:59:56 -04:00
|
|
|
ImagePixelFormatFunc = Runner->m_pSystem->ImagePixelFormatOverride;
|
|
|
|
|
|
2023-05-24 02:50:03 -04:00
|
|
|
return true;
|
2023-01-26 05:27:42 -05:00
|
|
|
}
|
|
|
|
|
|
2023-05-24 02:50:03 -04:00
|
|
|
|
|
|
|
|
void FImageResizeRelTask::DoWork()
|
|
|
|
|
{
|
|
|
|
|
// This runs on a random worker thread
|
|
|
|
|
MUTABLE_CPUPROFILER_SCOPE(FImageResizeRelTask);
|
|
|
|
|
|
2023-06-23 02:01:13 -04:00
|
|
|
// \TODO: Track allocs
|
2023-09-13 06:59:56 -04:00
|
|
|
FImageOperator ImOp = FImageOperator::GetDefault(ImagePixelFormatFunc);
|
2023-06-23 02:01:13 -04:00
|
|
|
ImOp.ImageResizeLinear(Result.get(), ImageCompressionQuality, Base.get());
|
2023-05-24 02:50:03 -04:00
|
|
|
|
|
|
|
|
int32 LodCount = Result->GetLODCount();
|
|
|
|
|
if (LodCount > 1)
|
|
|
|
|
{
|
|
|
|
|
FMipmapGenerationSettings mipSettings{};
|
|
|
|
|
ImageMipmapInPlace(ImageCompressionQuality, Result.get(), mipSettings);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2024-06-14 04:01:28 -04:00
|
|
|
bool FImageResizeRelTask::Complete(CodeRunner* Runner)
|
2023-01-26 05:27:42 -05:00
|
|
|
{
|
2023-05-24 02:50:03 -04:00
|
|
|
// This runs in the Runner thread
|
|
|
|
|
Runner->Release(Base);
|
|
|
|
|
|
|
|
|
|
// If didn't take a shortcut and set it already
|
|
|
|
|
if (Result)
|
|
|
|
|
{
|
|
|
|
|
Runner->StoreImage(Op, Result);
|
|
|
|
|
}
|
2024-06-14 04:01:28 -04:00
|
|
|
|
|
|
|
|
bool bSuccess = true;
|
|
|
|
|
return bSuccess;
|
2023-01-26 05:27:42 -05:00
|
|
|
}
|
|
|
|
|
|
2023-05-24 02:50:03 -04:00
|
|
|
|
2023-01-26 05:27:42 -05:00
|
|
|
//---------------------------------------------------------------------------------------------
|
|
|
|
|
//---------------------------------------------------------------------------------------------
|
|
|
|
|
//---------------------------------------------------------------------------------------------
|
|
|
|
|
class FImageInvertTask : public CodeRunner::FIssuedTask
|
|
|
|
|
{
|
|
|
|
|
public:
|
2023-05-24 02:50:03 -04:00
|
|
|
FImageInvertTask(const FScheduledOp& InOp, const OP::ImageInvertArgs& InArgs)
|
|
|
|
|
: FIssuedTask(InOp), Args(InArgs)
|
|
|
|
|
{}
|
2023-01-26 05:27:42 -05:00
|
|
|
|
|
|
|
|
// FIssuedTask interface
|
2024-06-14 04:01:28 -04:00
|
|
|
virtual bool Prepare(CodeRunner*, bool& bOutFailed) override;
|
|
|
|
|
virtual void DoWork() override;
|
|
|
|
|
virtual bool Complete(CodeRunner* Runner) override;
|
2023-01-26 05:27:42 -05:00
|
|
|
|
|
|
|
|
private:
|
2023-05-24 02:50:03 -04:00
|
|
|
Ptr<Image> Result;
|
|
|
|
|
OP::ImageInvertArgs Args;
|
2023-01-26 05:27:42 -05:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
2023-05-24 02:50:03 -04:00
|
|
|
bool FImageInvertTask::Prepare(CodeRunner* Runner, bool& bOutFailed)
|
2023-01-26 05:27:42 -05:00
|
|
|
{
|
2023-05-24 02:50:03 -04:00
|
|
|
// This runs in the mutable Runner thread
|
2023-11-27 04:53:23 -05:00
|
|
|
MUTABLE_CPUPROFILER_SCOPE(FImageInvertTask_Prepare);
|
2023-05-24 02:50:03 -04:00
|
|
|
bOutFailed = false;
|
|
|
|
|
|
|
|
|
|
Ptr<const Image> Source = Runner->LoadImage({ Args.base, Op.ExecutionIndex, Op.ExecutionOptions });
|
|
|
|
|
|
|
|
|
|
// Create destination data
|
|
|
|
|
Result = Runner->CloneOrTakeOver(Source);
|
|
|
|
|
return true;
|
2023-01-26 05:27:42 -05:00
|
|
|
}
|
|
|
|
|
|
2023-05-24 02:50:03 -04:00
|
|
|
|
2023-01-26 05:27:42 -05:00
|
|
|
void FImageInvertTask::DoWork()
|
|
|
|
|
{
|
2023-05-24 02:50:03 -04:00
|
|
|
// This runs on a random worker thread
|
2023-01-26 05:27:42 -05:00
|
|
|
MUTABLE_CPUPROFILER_SCOPE(FImageInvertTask);
|
|
|
|
|
|
2024-03-12 07:17:49 -04:00
|
|
|
ImageInvert(Result.get());
|
2023-01-26 05:27:42 -05:00
|
|
|
}
|
|
|
|
|
|
2023-05-24 02:50:03 -04:00
|
|
|
|
2024-06-14 04:01:28 -04:00
|
|
|
bool FImageInvertTask::Complete(CodeRunner* Runner)
|
2023-01-26 05:27:42 -05:00
|
|
|
{
|
2023-05-24 02:50:03 -04:00
|
|
|
// If we didn't take a shortcut
|
|
|
|
|
if (Result)
|
|
|
|
|
{
|
|
|
|
|
Runner->StoreImage(Op, Result);
|
|
|
|
|
}
|
2024-06-14 04:01:28 -04:00
|
|
|
|
|
|
|
|
bool bSuccess = true;
|
|
|
|
|
return bSuccess;
|
2023-01-26 05:27:42 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2022-09-26 15:12:13 -04:00
|
|
|
//---------------------------------------------------------------------------------------------
|
|
|
|
|
//---------------------------------------------------------------------------------------------
|
|
|
|
|
//---------------------------------------------------------------------------------------------
|
|
|
|
|
class FImageComposeTask : public CodeRunner::FIssuedTask
|
|
|
|
|
{
|
|
|
|
|
public:
|
2023-05-24 02:50:03 -04:00
|
|
|
FImageComposeTask(const FScheduledOp& InOp, const OP::ImageComposeArgs& InArgs, const Ptr<const Layout>& InLayout)
|
|
|
|
|
: FIssuedTask(InOp), Args(InArgs), Layout(InLayout)
|
|
|
|
|
{}
|
2022-09-26 15:12:13 -04:00
|
|
|
|
|
|
|
|
// FIssuedTask interface
|
2024-06-14 04:01:28 -04:00
|
|
|
virtual bool Prepare(CodeRunner*, bool& bOutFailed) override;
|
|
|
|
|
virtual void DoWork() override;
|
|
|
|
|
virtual bool Complete(CodeRunner*) override;
|
2022-09-26 15:12:13 -04:00
|
|
|
|
|
|
|
|
private:
|
2023-05-24 02:50:03 -04:00
|
|
|
int32 ImageCompressionQuality = 0;
|
2022-09-26 15:12:13 -04:00
|
|
|
OP::ImageComposeArgs Args;
|
2023-05-24 02:50:03 -04:00
|
|
|
Ptr<const Layout> Layout;
|
|
|
|
|
Ptr<const Image> Block;
|
|
|
|
|
Ptr<const Image> Mask;
|
|
|
|
|
Ptr<Image> Result;
|
2024-08-13 03:25:12 -04:00
|
|
|
box<FIntVector2> Rect;
|
2023-09-13 06:59:56 -04:00
|
|
|
FImageOperator::FImagePixelFormatFunc ImagePixelFormatFunc;
|
2022-09-26 15:12:13 -04:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
2023-05-24 02:50:03 -04:00
|
|
|
bool FImageComposeTask::Prepare(CodeRunner* Runner, bool& bOutFailed)
|
2022-09-26 15:12:13 -04:00
|
|
|
{
|
2023-05-24 02:50:03 -04:00
|
|
|
// This runs in the mutable Runner thread
|
2023-11-27 04:53:23 -05:00
|
|
|
MUTABLE_CPUPROFILER_SCOPE(FImageComposeTask_Prepare);
|
2023-05-24 02:50:03 -04:00
|
|
|
bOutFailed = false;
|
2022-09-26 15:12:13 -04:00
|
|
|
|
2023-05-24 02:50:03 -04:00
|
|
|
ImageCompressionQuality = Runner->m_pSettings->ImageCompressionQuality;
|
2022-09-26 15:12:13 -04:00
|
|
|
|
2023-05-24 02:50:03 -04:00
|
|
|
Ptr<const Image> Base = Runner->LoadImage({ Args.base, Op.ExecutionIndex, Op.ExecutionOptions });
|
|
|
|
|
|
2024-05-13 03:20:24 -04:00
|
|
|
int32 RelBlockIndex = Layout->FindBlock(Args.BlockId);
|
2023-05-24 02:50:03 -04:00
|
|
|
|
|
|
|
|
// Shortcuts
|
|
|
|
|
if (RelBlockIndex < 0)
|
2022-09-26 15:12:13 -04:00
|
|
|
{
|
2023-05-24 02:50:03 -04:00
|
|
|
Runner->StoreImage(Op, Base);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Only load the image is RelBlockIndex is valid, otherwise, we won't have requested it.
|
|
|
|
|
Block = Runner->LoadImage({ Args.blockImage, Op.ExecutionIndex, Op.ExecutionOptions });
|
|
|
|
|
if (Args.mask)
|
|
|
|
|
{
|
|
|
|
|
Mask = Runner->LoadImage({ Args.mask, Op.ExecutionIndex, Op.ExecutionOptions });
|
|
|
|
|
}
|
|
|
|
|
|
2024-08-13 03:25:12 -04:00
|
|
|
box<FIntVector2> RectInblocks;
|
2024-06-10 08:37:02 -04:00
|
|
|
RectInblocks.min = Layout->Blocks[RelBlockIndex].Min;
|
|
|
|
|
RectInblocks.size = Layout->Blocks[RelBlockIndex].Size;
|
2023-05-24 02:50:03 -04:00
|
|
|
|
|
|
|
|
// Convert the rect from blocks to pixels
|
|
|
|
|
FIntPoint Grid = Layout->GetGridSize();
|
|
|
|
|
int32 BlockSizeX = Base->GetSizeX() / Grid[0];
|
|
|
|
|
int32 BlockSizeY = Base->GetSizeY() / Grid[1];
|
|
|
|
|
Rect = RectInblocks;
|
|
|
|
|
Rect.min[0] *= BlockSizeX;
|
|
|
|
|
Rect.min[1] *= BlockSizeY;
|
|
|
|
|
Rect.size[0] *= BlockSizeX;
|
|
|
|
|
Rect.size[1] *= BlockSizeY;
|
|
|
|
|
|
|
|
|
|
if (!(Block && Rect.size[0] && Rect.size[1] && Block->GetSizeX() && Block->GetSizeY()))
|
|
|
|
|
{
|
|
|
|
|
Runner->Release(Block);
|
|
|
|
|
Runner->Release(Mask);
|
|
|
|
|
Runner->StoreImage(Op, Base);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Create destination data
|
|
|
|
|
Result = Runner->CloneOrTakeOver(Base);
|
|
|
|
|
Result->m_flags = 0;
|
|
|
|
|
|
|
|
|
|
bool useMask = Args.mask != 0;
|
|
|
|
|
if (!useMask)
|
|
|
|
|
{
|
|
|
|
|
MUTABLE_CPUPROFILER_SCOPE(ImageComposeWithoutMask);
|
|
|
|
|
|
2023-06-23 02:01:13 -04:00
|
|
|
FImageOperator ImOp = MakeImageOperator(Runner);
|
|
|
|
|
|
2023-05-24 02:50:03 -04:00
|
|
|
EImageFormat Format = GetMostGenericFormat(Result->GetFormat(), Block->GetFormat());
|
|
|
|
|
|
|
|
|
|
// Resize image if it doesn't fit in the new block size
|
2024-08-13 03:25:12 -04:00
|
|
|
if (FIntVector2(Block->GetSize()) != Rect.size)
|
2022-09-26 15:12:13 -04:00
|
|
|
{
|
2023-05-24 02:50:03 -04:00
|
|
|
MUTABLE_CPUPROFILER_SCOPE(ImageComposeWithoutMask_BlockResize);
|
|
|
|
|
|
|
|
|
|
// This now happens more often since the generation of specific mips on request. For this reason
|
|
|
|
|
// this warning is usually acceptable.
|
|
|
|
|
Ptr<Image> Resized = Runner->CreateImage(Rect.size[0], Rect.size[1], 1, Block->GetFormat(), EInitializationType::NotInitialized );
|
2023-06-23 02:01:13 -04:00
|
|
|
ImOp.ImageResizeLinear(Resized.get(), ImageCompressionQuality, Block.get());
|
2023-05-24 02:50:03 -04:00
|
|
|
Runner->Release(Block);
|
|
|
|
|
Block = Resized;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Change the block image format if it doesn't match the composed image
|
|
|
|
|
// This is usually enforced at object compilation time.
|
|
|
|
|
if (Result->GetFormat() != Block->GetFormat())
|
|
|
|
|
{
|
|
|
|
|
MUTABLE_CPUPROFILER_SCOPE(ImageComposeReformat);
|
|
|
|
|
|
|
|
|
|
if (Result->GetFormat() != Format)
|
|
|
|
|
{
|
|
|
|
|
Ptr<Image> Formatted = Runner->CreateImage(Result->GetSizeX(), Result->GetSizeY(), Result->GetLODCount(), Format, EInitializationType::NotInitialized);
|
|
|
|
|
bool bSuccess = false;
|
2023-06-23 02:01:13 -04:00
|
|
|
ImOp.ImagePixelFormat(bSuccess, ImageCompressionQuality, Formatted.get(), Result.get());
|
2023-05-24 02:50:03 -04:00
|
|
|
check(bSuccess); // Decompression cannot fail
|
|
|
|
|
Runner->Release(Result);
|
|
|
|
|
Result = Formatted;
|
|
|
|
|
}
|
|
|
|
|
if (Block->GetFormat() != Format)
|
|
|
|
|
{
|
|
|
|
|
Ptr<Image> Formatted = Runner->CreateImage(Block->GetSizeX(), Block->GetSizeY(), Block->GetLODCount(), Format, EInitializationType::NotInitialized);
|
|
|
|
|
bool bSuccess = false;
|
2023-06-23 02:01:13 -04:00
|
|
|
ImOp.ImagePixelFormat(bSuccess, ImageCompressionQuality, Formatted.get(), Block.get());
|
2023-05-24 02:50:03 -04:00
|
|
|
check(bSuccess); // Decompression cannot fail
|
|
|
|
|
Runner->Release(Block);
|
|
|
|
|
Block = Formatted;
|
|
|
|
|
}
|
2022-09-26 15:12:13 -04:00
|
|
|
}
|
|
|
|
|
}
|
2023-05-24 02:50:03 -04:00
|
|
|
|
2023-09-13 06:59:56 -04:00
|
|
|
ImagePixelFormatFunc = Runner->m_pSystem->ImagePixelFormatOverride;
|
|
|
|
|
|
2023-05-24 02:50:03 -04:00
|
|
|
return true;
|
2022-09-26 15:12:13 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void FImageComposeTask::DoWork()
|
|
|
|
|
{
|
2023-05-24 02:50:03 -04:00
|
|
|
// This runs on a random worker thread
|
2022-09-26 15:12:13 -04:00
|
|
|
MUTABLE_CPUPROFILER_SCOPE(FImageComposeTask);
|
|
|
|
|
|
2023-05-24 02:50:03 -04:00
|
|
|
bool useMask = Args.mask != 0;
|
|
|
|
|
if (!useMask)
|
2022-09-26 15:12:13 -04:00
|
|
|
{
|
2023-05-24 02:50:03 -04:00
|
|
|
MUTABLE_CPUPROFILER_SCOPE(ImageComposeWithoutMask);
|
|
|
|
|
|
|
|
|
|
// Compose without a mask
|
2023-06-23 02:01:13 -04:00
|
|
|
// \TODO: track allocs
|
2023-09-13 06:59:56 -04:00
|
|
|
FImageOperator ImOp = FImageOperator::GetDefault(ImagePixelFormatFunc);
|
2023-06-23 02:01:13 -04:00
|
|
|
ImOp.ImageCompose(Result.get(), Block.get(), Rect);
|
2022-09-26 15:12:13 -04:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2023-05-24 02:50:03 -04:00
|
|
|
MUTABLE_CPUPROFILER_SCOPE(ImageComposeWithMask);
|
2022-09-26 15:12:13 -04:00
|
|
|
|
2023-05-24 02:50:03 -04:00
|
|
|
// Compose with a mask
|
|
|
|
|
ImageBlendOnBaseNoAlpha(Result.get(), Mask.get(), Block.get(), Rect);
|
2022-09-26 15:12:13 -04:00
|
|
|
}
|
|
|
|
|
|
2023-05-24 02:50:03 -04:00
|
|
|
Layout = nullptr;
|
2022-09-26 15:12:13 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2024-06-14 04:01:28 -04:00
|
|
|
bool FImageComposeTask::Complete(CodeRunner* Runner)
|
2022-09-26 15:12:13 -04:00
|
|
|
{
|
2023-05-24 02:50:03 -04:00
|
|
|
// This runs in the mutable Runner thread
|
|
|
|
|
Runner->Release(Block);
|
|
|
|
|
Runner->Release(Mask);
|
|
|
|
|
|
|
|
|
|
// If we didn't take a shortcut
|
|
|
|
|
if (Result)
|
|
|
|
|
{
|
|
|
|
|
Runner->StoreImage(Op, Result);
|
|
|
|
|
}
|
2024-06-14 04:01:28 -04:00
|
|
|
|
|
|
|
|
bool bSuccess = true;
|
|
|
|
|
return bSuccess;
|
2022-09-26 15:12:13 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//---------------------------------------------------------------------------------------------
|
|
|
|
|
//---------------------------------------------------------------------------------------------
|
|
|
|
|
//---------------------------------------------------------------------------------------------
|
2023-07-24 06:17:34 -04:00
|
|
|
bool CodeRunner::FLoadMeshRomTask::Prepare(CodeRunner* Runner, bool& bOutFailed)
|
2022-09-26 15:12:13 -04:00
|
|
|
{
|
2023-07-24 06:17:34 -04:00
|
|
|
// This runs in the mutable Runner thread
|
|
|
|
|
MUTABLE_CPUPROFILER_SCOPE(FLoadMeshRomTask_Prepare);
|
|
|
|
|
|
2023-06-26 02:16:47 -04:00
|
|
|
if (!Runner || !Runner->m_pSystem)
|
|
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2022-09-26 15:12:13 -04:00
|
|
|
bOutFailed = false;
|
|
|
|
|
|
2023-07-24 06:17:34 -04:00
|
|
|
const FProgram& Program = Runner->m_pModel->GetPrivate()->m_program;
|
2024-08-13 10:23:49 -04:00
|
|
|
|
|
|
|
|
check(RomIndex < Program.m_roms.Num());
|
2023-07-24 06:17:34 -04:00
|
|
|
|
2024-08-13 10:23:49 -04:00
|
|
|
FWorkingMemoryManager::FModelCacheEntry* ModelCache = Runner->m_pSystem->WorkingMemoryManager.FindModelCache(Runner->m_pModel.Get());
|
|
|
|
|
++ModelCache->PendingOpsPerRom[RomIndex];
|
2023-07-24 06:17:34 -04:00
|
|
|
|
2024-08-13 10:23:49 -04:00
|
|
|
if (Program.IsRomLoaded(RomIndex))
|
2023-07-24 06:17:34 -04:00
|
|
|
{
|
2024-08-13 10:23:49 -04:00
|
|
|
return false;
|
2023-07-24 06:17:34 -04:00
|
|
|
}
|
|
|
|
|
|
2024-08-13 10:23:49 -04:00
|
|
|
if (const FRomLoadOp* Result = Runner->RomLoadOps.Find(RomIndex))
|
|
|
|
|
{
|
|
|
|
|
Event = Result->Event; // Wait for the read operation started by other task
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2023-07-24 06:17:34 -04:00
|
|
|
|
2024-08-13 10:23:49 -04:00
|
|
|
FRomLoadOp& RomLoadOp = Runner->RomLoadOps.Create(RomIndex);
|
|
|
|
|
|
|
|
|
|
check(Runner->m_pSystem->StreamInterface);
|
|
|
|
|
|
|
|
|
|
const uint32 RomSize = Program.m_roms[RomIndex].Size;
|
|
|
|
|
check(RomSize > 0);
|
|
|
|
|
|
|
|
|
|
// Free roms if necessary
|
|
|
|
|
{
|
|
|
|
|
MUTABLE_CPUPROFILER_SCOPE(FreeingRoms);
|
|
|
|
|
|
|
|
|
|
Runner->m_pSystem->WorkingMemoryManager.MarkRomUsed(RomIndex, Runner->m_pModel);
|
|
|
|
|
Runner->m_pSystem->WorkingMemoryManager.EnsureBudgetBelow(RomSize);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const int32 SizeBefore = RomLoadOp.m_streamBuffer.GetAllocatedSize();
|
|
|
|
|
RomLoadOp.m_streamBuffer.SetNumUninitialized(RomSize);
|
|
|
|
|
const int32 SizeAfter = RomLoadOp.m_streamBuffer.GetAllocatedSize();
|
|
|
|
|
|
|
|
|
|
UE::Tasks::FTaskEvent ReadCompletionEvent = UE::Tasks::FTaskEvent(TEXT("FLoadMeshRomTask"));
|
|
|
|
|
RomLoadOp.Event = ReadCompletionEvent;
|
|
|
|
|
|
|
|
|
|
TFunction<void(bool)> Callback = [ReadCompletionEvent](bool bSuccess) mutable
|
|
|
|
|
{
|
|
|
|
|
ReadCompletionEvent.Trigger();
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const uint32 RomId = Program.m_roms[RomIndex].Id;
|
|
|
|
|
RomLoadOp.m_streamID = Runner->m_pSystem->StreamInterface->BeginReadBlock(Runner->m_pModel.Get(), RomId, RomLoadOp.m_streamBuffer.GetData(), RomSize, &Callback);
|
|
|
|
|
if (RomLoadOp.m_streamID < 0)
|
|
|
|
|
{
|
|
|
|
|
bOutFailed = true;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Event = ReadCompletionEvent; // Wait for read operation to end
|
2023-07-24 06:17:34 -04:00
|
|
|
return false; // No worker thread work
|
2022-09-26 15:12:13 -04:00
|
|
|
}
|
2023-07-24 06:17:34 -04:00
|
|
|
|
2022-09-26 15:12:13 -04:00
|
|
|
|
|
|
|
|
//---------------------------------------------------------------------------------------------
|
2024-06-14 04:01:28 -04:00
|
|
|
bool CodeRunner::FLoadMeshRomTask::Complete(CodeRunner* Runner)
|
2022-09-26 15:12:13 -04:00
|
|
|
{
|
2023-07-24 06:17:34 -04:00
|
|
|
// This runs in the Runner thread
|
|
|
|
|
MUTABLE_CPUPROFILER_SCOPE(FLoadMeshRomTask_Complete);
|
|
|
|
|
|
2023-06-26 02:16:47 -04:00
|
|
|
if (!Runner || !Runner->m_pSystem)
|
|
|
|
|
{
|
2024-06-14 04:01:28 -04:00
|
|
|
return false;
|
2023-06-26 02:16:47 -04:00
|
|
|
}
|
|
|
|
|
|
2023-07-24 06:17:34 -04:00
|
|
|
FProgram& Program = Runner->m_pModel->GetPrivate()->m_program;
|
2024-08-13 10:23:49 -04:00
|
|
|
|
|
|
|
|
// Since task could be reordered, we need to make sure we end the rom read before continuing
|
|
|
|
|
if (FRomLoadOp* RomLoadOp = Runner->RomLoadOps.Find(RomIndex))
|
|
|
|
|
{
|
|
|
|
|
Runner->m_pSystem->StreamInterface->EndRead(RomLoadOp->m_streamID);
|
|
|
|
|
|
|
|
|
|
const int32 ResIndex = Program.m_roms[RomIndex].ResourceIndex;
|
|
|
|
|
check(!Program.ConstantMeshes[ResIndex].Value)
|
|
|
|
|
MUTABLE_CPUPROFILER_SCOPE(Unserialise);
|
|
|
|
|
|
|
|
|
|
InputMemoryStream Stream(RomLoadOp->m_streamBuffer.GetData(), RomLoadOp->m_streamBuffer.Num());
|
|
|
|
|
InputArchive Arch(&Stream);
|
|
|
|
|
|
|
|
|
|
check(!Program.ConstantMeshes[ResIndex].Value);
|
|
|
|
|
Ptr<Mesh> Value = Mesh::StaticUnserialise(Arch);
|
|
|
|
|
Program.SetMeshRomValue(RomIndex, Value);
|
|
|
|
|
|
|
|
|
|
check(Program.ConstantMeshes[ResIndex].Value);
|
|
|
|
|
|
|
|
|
|
Runner->RomLoadOps.Remove(*RomLoadOp);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Process the constant op normally, now that the rom is loaded.
|
|
|
|
|
Runner->RunCode(Op, Runner->m_pParams, Runner->m_pModel, Runner->m_lodMask);
|
|
|
|
|
|
2024-08-13 05:15:15 -04:00
|
|
|
FWorkingMemoryManager::FModelCacheEntry* ModelCache = Runner->m_pSystem->WorkingMemoryManager.FindModelCache(Runner->m_pModel.Get());
|
2023-07-24 06:17:34 -04:00
|
|
|
|
2024-08-13 10:23:49 -04:00
|
|
|
Runner->m_pSystem->WorkingMemoryManager.MarkRomUsed(RomIndex, Runner->m_pModel);
|
|
|
|
|
--ModelCache->PendingOpsPerRom[RomIndex];
|
2024-06-14 04:01:28 -04:00
|
|
|
|
2024-08-13 10:23:49 -04:00
|
|
|
bool bSuccess = true;
|
2024-06-14 04:01:28 -04:00
|
|
|
return bSuccess;
|
2022-09-26 15:12:13 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2023-04-05 11:20:44 -04:00
|
|
|
//---------------------------------------------------------------------------------------------
|
|
|
|
|
//---------------------------------------------------------------------------------------------
|
|
|
|
|
//---------------------------------------------------------------------------------------------
|
2023-05-24 02:50:03 -04:00
|
|
|
bool CodeRunner::FLoadExtensionDataTask::Prepare(CodeRunner* Runner, bool& bOutFailed)
|
2023-04-05 11:20:44 -04:00
|
|
|
{
|
2023-05-24 02:50:03 -04:00
|
|
|
// This runs in the mutable Runner thread
|
2023-04-05 11:20:44 -04:00
|
|
|
MUTABLE_CPUPROFILER_SCOPE(FLoadExtensionDataTask_Prepare);
|
2023-05-24 02:50:03 -04:00
|
|
|
bOutFailed = false;
|
2023-04-05 11:20:44 -04:00
|
|
|
|
|
|
|
|
FProgram& Program = Runner->m_pModel->GetPrivate()->m_program;
|
|
|
|
|
check(Program.m_constantExtensionData.IsValidIndex(ModelConstantIndex));
|
|
|
|
|
|
|
|
|
|
const FExtensionDataConstant& Constant = Program.m_constantExtensionData[ModelConstantIndex];
|
|
|
|
|
check(Constant.Data == Data);
|
|
|
|
|
|
|
|
|
|
if (Constant.LoadState == FExtensionDataConstant::ELoadState::FailedToLoad)
|
|
|
|
|
{
|
|
|
|
|
// This already failed before, so don't waste time trying again.
|
|
|
|
|
//
|
|
|
|
|
// This behavior could be made an option in future if needed.
|
|
|
|
|
bOutFailed = true;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TArray<ExtensionDataPtrConst> UnloadedConstants;
|
|
|
|
|
LoadHandle = Runner->m_pSystem->GetExtensionDataStreamer()->StartLoad(Data, UnloadedConstants);
|
|
|
|
|
check(LoadHandle.IsValid());
|
|
|
|
|
|
|
|
|
|
// Update the load state of any constants that were unloaded
|
|
|
|
|
for (const ExtensionDataPtrConst& Unloaded : UnloadedConstants)
|
|
|
|
|
{
|
|
|
|
|
check(Unloaded->Origin == ExtensionData::EOrigin::ConstantStreamed);
|
|
|
|
|
|
|
|
|
|
FExtensionDataConstant* FoundConstant = Algo::FindBy(Program.m_constantExtensionData, Unloaded, &FExtensionDataConstant::Data);
|
|
|
|
|
check(FoundConstant);
|
|
|
|
|
|
|
|
|
|
FoundConstant->LoadState = FExtensionDataConstant::ELoadState::Unloaded;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// No worker thread work
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//---------------------------------------------------------------------------------------------
|
2024-06-14 04:01:28 -04:00
|
|
|
bool CodeRunner::FLoadExtensionDataTask::Complete(CodeRunner* Runner)
|
2023-04-05 11:20:44 -04:00
|
|
|
{
|
|
|
|
|
MUTABLE_CPUPROFILER_SCOPE(FLoadExtensionDataTask_Complete);
|
|
|
|
|
|
2024-06-14 04:01:28 -04:00
|
|
|
bool bSuccess = true;
|
|
|
|
|
|
2023-04-05 11:20:44 -04:00
|
|
|
// Complete should only be called if the load is finished
|
|
|
|
|
check(LoadHandle->LoadState != FExtensionDataLoadHandle::ELoadState::Pending);
|
|
|
|
|
|
|
|
|
|
FProgram& Program = Runner->m_pModel->GetPrivate()->m_program;
|
|
|
|
|
check(Program.m_constantExtensionData.IsValidIndex(ModelConstantIndex));
|
|
|
|
|
|
|
|
|
|
FExtensionDataConstant& Constant = Program.m_constantExtensionData[ModelConstantIndex];
|
|
|
|
|
check(Constant.Data == Data);
|
|
|
|
|
|
|
|
|
|
// Update the load state in the Program
|
|
|
|
|
if (LoadHandle->LoadState == FExtensionDataLoadHandle::ELoadState::Loaded)
|
|
|
|
|
{
|
|
|
|
|
Constant.LoadState = FExtensionDataConstant::ELoadState::CurrentlyLoaded;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
check(LoadHandle->LoadState == FExtensionDataLoadHandle::ELoadState::FailedToLoad);
|
|
|
|
|
Constant.LoadState = FExtensionDataConstant::ELoadState::FailedToLoad;
|
2024-06-14 04:01:28 -04:00
|
|
|
bSuccess = false;
|
2023-04-05 11:20:44 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Process the constant op normally, now that the data is loaded.
|
2023-08-03 03:37:44 -04:00
|
|
|
Runner->RunCode(Op, Runner->m_pParams, Runner->m_pModel, Runner->m_lodMask);
|
2024-06-14 04:01:28 -04:00
|
|
|
|
|
|
|
|
return bSuccess;
|
2023-04-05 11:20:44 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//---------------------------------------------------------------------------------------------
|
|
|
|
|
bool CodeRunner::FLoadExtensionDataTask::IsComplete(CodeRunner* Runner)
|
|
|
|
|
{
|
|
|
|
|
check(LoadHandle.IsValid());
|
|
|
|
|
return LoadHandle->LoadState != FExtensionDataLoadHandle::ELoadState::Pending;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2022-09-26 15:12:13 -04:00
|
|
|
//---------------------------------------------------------------------------------------------
|
|
|
|
|
//---------------------------------------------------------------------------------------------
|
|
|
|
|
//---------------------------------------------------------------------------------------------
|
2023-05-24 02:50:03 -04:00
|
|
|
bool CodeRunner::FLoadImageRomsTask::Prepare(CodeRunner* Runner, bool& bOutFailed )
|
2022-09-26 15:12:13 -04:00
|
|
|
{
|
2023-06-26 02:16:47 -04:00
|
|
|
if (!Runner || !Runner->m_pSystem)
|
|
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2023-05-24 02:50:03 -04:00
|
|
|
// This runs in the mutable Runner thread
|
2022-09-26 15:12:13 -04:00
|
|
|
MUTABLE_CPUPROFILER_SCOPE(FLoadImageRomsTask_Prepare);
|
|
|
|
|
bOutFailed = false;
|
|
|
|
|
|
2023-05-24 02:50:03 -04:00
|
|
|
FProgram& program = Runner->m_pModel->GetPrivate()->m_program;
|
|
|
|
|
|
2023-06-26 02:16:47 -04:00
|
|
|
FWorkingMemoryManager::FModelCacheEntry* ModelCache = Runner->m_pSystem->WorkingMemoryManager.FindModelCache(Runner->m_pModel.Get());
|
2022-09-26 15:12:13 -04:00
|
|
|
|
2023-07-24 06:17:34 -04:00
|
|
|
TArray<UE::Tasks::FTask> ReadCompleteEvents;
|
|
|
|
|
|
|
|
|
|
ReadCompleteEvents.Reserve(LODIndexCount);
|
|
|
|
|
|
|
|
|
|
for (int32 LODIndex = 0; LODIndex < LODIndexCount; ++LODIndex)
|
2022-09-26 15:12:13 -04:00
|
|
|
{
|
2023-07-24 06:17:34 -04:00
|
|
|
const int32 CurrentIndexIndex = LODIndexIndex + LODIndex;
|
2024-06-13 04:48:11 -04:00
|
|
|
const int32 CurrentIndex = program.ConstantImageLODIndices[CurrentIndexIndex];
|
2022-09-26 15:12:13 -04:00
|
|
|
|
2024-02-27 04:56:39 -05:00
|
|
|
if (program.ConstantImageLODs[CurrentIndex].Key < 0)
|
2022-09-26 15:12:13 -04:00
|
|
|
{
|
|
|
|
|
// This data is always resident.
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-27 04:56:39 -05:00
|
|
|
int32 RomIndex = program.ConstantImageLODs[CurrentIndex].Key;
|
2022-09-26 15:12:13 -04:00
|
|
|
check(RomIndex < program.m_roms.Num());
|
|
|
|
|
|
2023-06-26 02:16:47 -04:00
|
|
|
++ModelCache->PendingOpsPerRom[RomIndex];
|
2022-09-26 15:12:13 -04:00
|
|
|
|
|
|
|
|
if (DebugRom && (DebugRomAll || RomIndex == DebugRomIndex))
|
2023-06-26 02:16:47 -04:00
|
|
|
UE_LOG(LogMutableCore, Log, TEXT("Preparing rom %d, now peding ops is %d."), RomIndex, ModelCache->PendingOpsPerRom[RomIndex]);
|
2022-09-26 15:12:13 -04:00
|
|
|
|
2023-07-24 06:17:34 -04:00
|
|
|
if (program.IsRomLoaded(RomIndex))
|
2022-09-26 15:12:13 -04:00
|
|
|
{
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2023-07-24 06:17:34 -04:00
|
|
|
RomIndices.Add(RomIndex);
|
|
|
|
|
|
|
|
|
|
if (const FRomLoadOp* Result = Runner->RomLoadOps.Find(RomIndex))
|
|
|
|
|
{
|
|
|
|
|
ReadCompleteEvents.Add(Result->Event); // Wait for the read operation started by other task
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
FRomLoadOp& RomLoadOp = Runner->RomLoadOps.Create(RomIndex);
|
|
|
|
|
|
2023-05-24 02:50:03 -04:00
|
|
|
check(Runner->m_pSystem->StreamInterface);
|
2022-09-26 15:12:13 -04:00
|
|
|
|
2023-07-24 06:17:34 -04:00
|
|
|
const uint32 RomSize = program.m_roms[RomIndex].Size;
|
2022-09-26 15:12:13 -04:00
|
|
|
check(RomSize > 0);
|
|
|
|
|
|
2023-05-24 02:50:03 -04:00
|
|
|
// Free roms if necessary
|
|
|
|
|
{
|
|
|
|
|
Runner->m_pSystem->WorkingMemoryManager.MarkRomUsed(RomIndex, Runner->m_pModel);
|
|
|
|
|
Runner->m_pSystem->WorkingMemoryManager.EnsureBudgetBelow(RomSize);
|
|
|
|
|
}
|
|
|
|
|
|
2023-07-24 06:17:34 -04:00
|
|
|
const int32 SizeBefore = RomLoadOp.m_streamBuffer.GetAllocatedSize();
|
|
|
|
|
RomLoadOp.m_streamBuffer.SetNumUninitialized(RomSize);
|
|
|
|
|
const int32 SizeAfter = RomLoadOp.m_streamBuffer.GetAllocatedSize();
|
|
|
|
|
|
2024-03-01 05:39:11 -05:00
|
|
|
UE::Tasks::FTaskEvent ReadCompletionEvent(TEXT("FLoadImageRomsTaskRom"));
|
|
|
|
|
ReadCompleteEvents.Add(ReadCompletionEvent);
|
|
|
|
|
RomLoadOp.Event = ReadCompletionEvent;
|
2023-07-24 06:17:34 -04:00
|
|
|
|
2024-03-01 05:39:11 -05:00
|
|
|
TFunction<void(bool)> Callback = [ReadCompletionEvent](bool bSuccess) mutable // Mutable due Trigger not being const
|
2022-09-26 15:12:13 -04:00
|
|
|
{
|
2024-03-01 05:39:11 -05:00
|
|
|
ReadCompletionEvent.Trigger();
|
2023-07-24 06:17:34 -04:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const uint32 RomId = program.m_roms[RomIndex].Id;
|
|
|
|
|
RomLoadOp.m_streamID = Runner->m_pSystem->StreamInterface->BeginReadBlock(Runner->m_pModel.Get(), RomId, RomLoadOp.m_streamBuffer.GetData(), RomSize, &Callback);
|
|
|
|
|
if (RomLoadOp.m_streamID < 0)
|
2022-09-26 15:12:13 -04:00
|
|
|
{
|
|
|
|
|
bOutFailed = true;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-07-24 06:17:34 -04:00
|
|
|
// Wait for all read operations to end
|
2024-03-01 05:39:11 -05:00
|
|
|
UE::Tasks::FTaskEvent GatherReadsCompletionEvent(TEXT("FLoadImageRomsTask"));
|
|
|
|
|
GatherReadsCompletionEvent.AddPrerequisites(ReadCompleteEvents);
|
|
|
|
|
GatherReadsCompletionEvent.Trigger();
|
|
|
|
|
|
|
|
|
|
Event = GatherReadsCompletionEvent;
|
2023-07-24 06:17:34 -04:00
|
|
|
|
|
|
|
|
return false; // No worker thread work
|
2022-09-26 15:12:13 -04:00
|
|
|
}
|
2023-07-24 06:17:34 -04:00
|
|
|
|
|
|
|
|
|
2022-09-26 15:12:13 -04:00
|
|
|
//---------------------------------------------------------------------------------------------
|
2024-06-14 04:01:28 -04:00
|
|
|
bool CodeRunner::FLoadImageRomsTask::Complete(CodeRunner* Runner)
|
2022-09-26 15:12:13 -04:00
|
|
|
{
|
2023-07-24 06:17:34 -04:00
|
|
|
// This runs in the Runner thread
|
|
|
|
|
MUTABLE_CPUPROFILER_SCOPE(FLoadImageRomsTask_Complete);
|
|
|
|
|
|
2023-06-26 02:16:47 -04:00
|
|
|
if (!Runner || !Runner->m_pSystem)
|
|
|
|
|
{
|
2024-06-14 04:01:28 -04:00
|
|
|
return false;
|
2023-06-26 02:16:47 -04:00
|
|
|
}
|
|
|
|
|
|
2024-02-27 04:56:39 -05:00
|
|
|
FProgram& Program = Runner->m_pModel->GetPrivate()->m_program;
|
2023-07-24 06:17:34 -04:00
|
|
|
|
2023-06-26 02:16:47 -04:00
|
|
|
FWorkingMemoryManager::FModelCacheEntry* ModelCache = Runner->m_pSystem->WorkingMemoryManager.FindModelCache(Runner->m_pModel.Get());
|
2022-09-26 15:12:13 -04:00
|
|
|
|
2024-06-14 04:01:28 -04:00
|
|
|
bool bSomeMissingData = false;
|
|
|
|
|
|
2023-07-24 06:17:34 -04:00
|
|
|
for (const int32 RomIndex : RomIndices)
|
|
|
|
|
{
|
|
|
|
|
// Since task could be reordered, we need to make sure we end the rom read before continuing
|
|
|
|
|
if (FRomLoadOp* RomLoadOp = Runner->RomLoadOps.Find(RomIndex))
|
|
|
|
|
{
|
2024-06-14 04:01:28 -04:00
|
|
|
bool bSuccess = Runner->m_pSystem->StreamInterface->EndRead(RomLoadOp->m_streamID);
|
2023-07-24 06:17:34 -04:00
|
|
|
|
|
|
|
|
MUTABLE_CPUPROFILER_SCOPE(Unserialise);
|
|
|
|
|
|
2024-06-14 04:01:28 -04:00
|
|
|
if (bSuccess)
|
|
|
|
|
{
|
|
|
|
|
InputMemoryStream Stream(RomLoadOp->m_streamBuffer.GetData(), RomLoadOp->m_streamBuffer.Num());
|
|
|
|
|
InputArchive Arch(&Stream);
|
2023-07-24 06:17:34 -04:00
|
|
|
|
2024-06-14 04:01:28 -04:00
|
|
|
const int32 ResIndex = Program.m_roms[RomIndex].ResourceIndex;
|
2023-07-24 06:17:34 -04:00
|
|
|
|
2024-06-14 04:01:28 -04:00
|
|
|
// TODO: Try to reuse buffer from PooledImages.
|
|
|
|
|
check(!Program.ConstantImageLODs[ResIndex].Value);
|
|
|
|
|
Ptr<Image> Value = Image::StaticUnserialise(Arch);
|
2024-02-27 04:56:39 -05:00
|
|
|
|
2024-06-14 04:01:28 -04:00
|
|
|
Program.SetImageRomValue(RomIndex, Value);
|
|
|
|
|
check(Program.ConstantImageLODs[ResIndex].Value);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
bSomeMissingData = true;
|
|
|
|
|
}
|
2023-07-24 06:17:34 -04:00
|
|
|
|
|
|
|
|
Runner->RomLoadOps.Remove(*RomLoadOp);
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-09-26 15:12:13 -04:00
|
|
|
|
2024-06-14 04:01:28 -04:00
|
|
|
if (bSomeMissingData)
|
|
|
|
|
{
|
|
|
|
|
// Some data may be missing. We can try to go on if some mips are there.
|
|
|
|
|
UE_LOG(LogMutableCore, Verbose, TEXT("FLoadImageRomsTask::Complete failed: missing data?"));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Process the constant op normally, now that the rom is loaded.
|
|
|
|
|
bool bSuccess = Runner->RunCode_ConstantResource(Op, Runner->m_pModel.Get());
|
|
|
|
|
|
|
|
|
|
for (int32 LODIndex = 0; bSuccess && (LODIndex < LODIndexCount); ++LODIndex)
|
2022-09-26 15:12:13 -04:00
|
|
|
{
|
2023-07-24 06:17:34 -04:00
|
|
|
int32 CurrentIndexIndex = LODIndexIndex + LODIndex;
|
2024-06-13 04:48:11 -04:00
|
|
|
int32 CurrentIndex = Program.ConstantImageLODIndices[CurrentIndexIndex];
|
2023-07-19 08:16:15 -04:00
|
|
|
|
2024-02-27 04:56:39 -05:00
|
|
|
if (Program.ConstantImageLODs[CurrentIndex].Key < 0)
|
2023-07-19 08:16:15 -04:00
|
|
|
{
|
|
|
|
|
// This data is always resident.
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-27 04:56:39 -05:00
|
|
|
int32 RomIndex = Program.ConstantImageLODs[CurrentIndex].Key;
|
|
|
|
|
check(RomIndex < Program.m_roms.Num());
|
2023-07-19 08:16:15 -04:00
|
|
|
|
2023-05-24 02:50:03 -04:00
|
|
|
Runner->m_pSystem->WorkingMemoryManager.MarkRomUsed(RomIndex, Runner->m_pModel);
|
2023-06-26 02:16:47 -04:00
|
|
|
--ModelCache->PendingOpsPerRom[RomIndex];
|
2022-09-26 15:12:13 -04:00
|
|
|
|
2023-06-20 04:26:21 -04:00
|
|
|
if (DebugRom && (DebugRomAll || RomIndex == DebugRomIndex))
|
|
|
|
|
{
|
2023-06-26 02:16:47 -04:00
|
|
|
UE_LOG(LogMutableCore, Log, TEXT("FLoadImageRomsTask::Complete rom %d, now peding ops is %d."), RomIndex, ModelCache->PendingOpsPerRom[RomIndex]);
|
2023-06-20 04:26:21 -04:00
|
|
|
}
|
2022-09-26 15:12:13 -04:00
|
|
|
}
|
2024-06-14 04:01:28 -04:00
|
|
|
|
|
|
|
|
return bSuccess;
|
2022-09-26 15:12:13 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2023-10-05 04:46:46 -04:00
|
|
|
/** This task is used to load an image parameter (by its FName) or an image reference (from its ID).
|
|
|
|
|
*/
|
2023-01-13 10:48:15 -05:00
|
|
|
class FImageExternalLoadTask : public CodeRunner::FIssuedTask
|
|
|
|
|
{
|
|
|
|
|
public:
|
2023-10-05 04:46:46 -04:00
|
|
|
|
2024-05-14 07:28:21 -04:00
|
|
|
FImageExternalLoadTask(const FScheduledOp& InItem, uint8 InMipmapsToSkip, CodeRunner::FExternalResourceId InId);
|
2023-01-13 10:48:15 -05:00
|
|
|
|
|
|
|
|
// FIssuedTask interface
|
2023-05-24 02:50:03 -04:00
|
|
|
virtual bool Prepare(CodeRunner*, bool& bOutFailed) override;
|
2024-06-14 04:01:28 -04:00
|
|
|
virtual bool Complete(CodeRunner* Runner) override;
|
2023-01-13 10:48:15 -05:00
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
uint8 MipmapsToSkip;
|
2024-05-14 07:28:21 -04:00
|
|
|
CodeRunner::FExternalResourceId Id;
|
2023-01-13 10:48:15 -05:00
|
|
|
|
|
|
|
|
Ptr<Image> Result;
|
|
|
|
|
|
|
|
|
|
TFunction<void()> ExternalCleanUpFunc;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
2024-05-14 07:28:21 -04:00
|
|
|
FImageExternalLoadTask::FImageExternalLoadTask(const FScheduledOp& InOp, uint8 InMipmapsToSkip, CodeRunner::FExternalResourceId InId)
|
2023-02-14 12:12:46 -05:00
|
|
|
: FIssuedTask(InOp)
|
2023-01-13 10:48:15 -05:00
|
|
|
{
|
|
|
|
|
MipmapsToSkip = InMipmapsToSkip;
|
|
|
|
|
Id = InId;
|
|
|
|
|
}
|
|
|
|
|
|
2023-10-05 04:46:46 -04:00
|
|
|
|
2023-05-24 02:50:03 -04:00
|
|
|
bool FImageExternalLoadTask::Prepare(CodeRunner* Runner, bool& bOutFailed)
|
2023-01-13 10:48:15 -05:00
|
|
|
{
|
2023-05-24 02:50:03 -04:00
|
|
|
// This runs in the mutable Runner thread
|
2024-05-14 07:28:21 -04:00
|
|
|
MUTABLE_CPUPROFILER_SCOPE(FImageExternalLoadTask_Prepare);
|
2023-05-24 02:50:03 -04:00
|
|
|
|
2023-01-13 10:48:15 -05:00
|
|
|
// LoadExternalImageAsync will always generate some image even if it is a dummy one.
|
|
|
|
|
bOutFailed = false;
|
|
|
|
|
|
|
|
|
|
// Capturing this here should not be a problem. The lifetime of the callback lambda is tied to
|
|
|
|
|
// the task and the later will always outlive the former.
|
|
|
|
|
|
|
|
|
|
// Probably we could simply pass a reference to the result image.
|
|
|
|
|
TFunction<void (Ptr<Image>)> ResultCallback = [this](Ptr<Image> InResult)
|
|
|
|
|
{
|
|
|
|
|
Result = InResult;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
Tie(Event, ExternalCleanUpFunc) = Runner->LoadExternalImageAsync(Id, MipmapsToSkip, ResultCallback);
|
|
|
|
|
|
|
|
|
|
// return false indicating there is no work to do so Event is not overriden by a DoWork task.
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2023-10-05 04:46:46 -04:00
|
|
|
|
2024-06-14 04:01:28 -04:00
|
|
|
bool FImageExternalLoadTask::Complete(CodeRunner* Runner)
|
2023-01-13 10:48:15 -05:00
|
|
|
{
|
|
|
|
|
if (ExternalCleanUpFunc)
|
|
|
|
|
{
|
|
|
|
|
Invoke(ExternalCleanUpFunc);
|
|
|
|
|
}
|
|
|
|
|
|
2023-05-24 02:50:03 -04:00
|
|
|
Runner->StoreImage(Op, Result);
|
2024-06-14 04:01:28 -04:00
|
|
|
|
|
|
|
|
bool bSuccess = true;
|
|
|
|
|
return bSuccess;
|
2023-01-13 10:48:15 -05:00
|
|
|
}
|
2024-05-14 07:28:21 -04:00
|
|
|
|
|
|
|
|
|
|
|
|
|
/** This task is used to load a mesh parameter (by its FName) or a mesh reference (from its ID).
|
|
|
|
|
*/
|
|
|
|
|
class FMeshExternalLoadTask : public CodeRunner::FIssuedTask
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
|
|
|
|
|
FMeshExternalLoadTask(const FScheduledOp&, CodeRunner::FExternalResourceId);
|
|
|
|
|
|
|
|
|
|
// FIssuedTask interface
|
|
|
|
|
virtual bool Prepare(CodeRunner*, bool& bOutFailed) override;
|
2024-06-14 04:01:28 -04:00
|
|
|
virtual bool Complete(CodeRunner* Runner) override;
|
2024-05-14 07:28:21 -04:00
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
uint8 MipmapsToSkip;
|
|
|
|
|
CodeRunner::FExternalResourceId Id;
|
|
|
|
|
|
|
|
|
|
Ptr<Mesh> Result;
|
|
|
|
|
|
|
|
|
|
TFunction<void()> ExternalCleanUpFunc;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
FMeshExternalLoadTask::FMeshExternalLoadTask(const FScheduledOp& InOp, CodeRunner::FExternalResourceId InId)
|
|
|
|
|
: FIssuedTask(InOp)
|
|
|
|
|
, Id(InId)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
bool FMeshExternalLoadTask::Prepare(CodeRunner* Runner, bool& bOutFailed)
|
|
|
|
|
{
|
|
|
|
|
// This runs in the mutable Runner thread
|
|
|
|
|
MUTABLE_CPUPROFILER_SCOPE(FMeshExternalLoadTask_Prepare);
|
|
|
|
|
|
|
|
|
|
// LoadExternalMeshAsync will always generate some mesh even if it is a dummy one.
|
|
|
|
|
bOutFailed = false;
|
|
|
|
|
|
|
|
|
|
// Capturing this here should not be a problem. The lifetime of the callback lambda is tied to
|
|
|
|
|
// the task and the later will always outlive the former.
|
|
|
|
|
|
|
|
|
|
// Probably we could simply pass a reference to the result mesh.
|
|
|
|
|
TFunction<void(Ptr<Mesh>)> ResultCallback = [this](Ptr<Mesh> InResult)
|
|
|
|
|
{
|
|
|
|
|
Result = InResult;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
Tie(Event, ExternalCleanUpFunc) = Runner->LoadExternalMeshAsync(Id, ResultCallback);
|
|
|
|
|
|
|
|
|
|
// return false indicating there is no work to do so Event is not overriden by a DoWork task.
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2024-06-14 04:01:28 -04:00
|
|
|
bool FMeshExternalLoadTask::Complete(CodeRunner* Runner)
|
2024-05-14 07:28:21 -04:00
|
|
|
{
|
|
|
|
|
if (ExternalCleanUpFunc)
|
|
|
|
|
{
|
|
|
|
|
Invoke(ExternalCleanUpFunc);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Runner->StoreMesh(Op, Result);
|
2024-06-14 04:01:28 -04:00
|
|
|
|
|
|
|
|
bool bSuccess = true;
|
|
|
|
|
return bSuccess;
|
2024-05-14 07:28:21 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2024-08-13 10:23:49 -04:00
|
|
|
//---------------------------------------------------------------------------------------------
|
|
|
|
|
//---------------------------------------------------------------------------------------------
|
|
|
|
|
//---------------------------------------------------------------------------------------------
|
|
|
|
|
TSharedPtr<CodeRunner::FIssuedTask> CodeRunner::IssueOp(FScheduledOp item)
|
2022-09-26 15:12:13 -04:00
|
|
|
{
|
|
|
|
|
TSharedPtr<FIssuedTask> Issued;
|
|
|
|
|
|
2024-08-13 10:23:49 -04:00
|
|
|
FProgram& program = m_pModel->GetPrivate()->m_program;
|
2022-09-26 15:12:13 -04:00
|
|
|
|
2024-08-13 10:23:49 -04:00
|
|
|
OP_TYPE type = program.GetOpType(item.At);
|
2022-09-26 15:12:13 -04:00
|
|
|
|
2024-08-13 10:23:49 -04:00
|
|
|
switch (type)
|
2022-09-26 15:12:13 -04:00
|
|
|
{
|
|
|
|
|
case OP_TYPE::ME_CONSTANT:
|
|
|
|
|
{
|
2024-08-13 10:23:49 -04:00
|
|
|
OP::MeshConstantArgs args = program.GetOpArgs<OP::MeshConstantArgs>(item.At);
|
|
|
|
|
int32 RomIndex = program.ConstantMeshes[args.value].Key;
|
|
|
|
|
if (RomIndex >= 0 && !program.ConstantMeshes[args.value].Value)
|
2022-09-26 15:12:13 -04:00
|
|
|
{
|
2024-08-13 10:23:49 -04:00
|
|
|
Issued = MakeShared<FLoadMeshRomTask>(item, RomIndex);
|
2022-09-26 15:12:13 -04:00
|
|
|
}
|
2024-08-13 10:23:49 -04:00
|
|
|
else
|
2022-09-26 15:12:13 -04:00
|
|
|
{
|
2024-08-13 10:23:49 -04:00
|
|
|
// If already available, the rest of the constant code will run right away.
|
2022-09-26 15:12:13 -04:00
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
2023-04-05 11:20:44 -04:00
|
|
|
|
|
|
|
|
case OP_TYPE::ED_CONSTANT:
|
|
|
|
|
{
|
2024-08-13 10:23:49 -04:00
|
|
|
OP::ResourceConstantArgs Args = program.GetOpArgs<OP::ResourceConstantArgs>(item.At);
|
2023-04-05 11:20:44 -04:00
|
|
|
|
2024-08-13 10:23:49 -04:00
|
|
|
const FExtensionDataConstant::ELoadState LoadState = program.m_constantExtensionData[Args.value].LoadState;
|
2023-04-05 11:20:44 -04:00
|
|
|
|
|
|
|
|
check(LoadState != FExtensionDataConstant::ELoadState::Invalid);
|
|
|
|
|
|
|
|
|
|
if (LoadState == FExtensionDataConstant::ELoadState::AlwaysLoaded
|
|
|
|
|
|| LoadState == FExtensionDataConstant::ELoadState::CurrentlyLoaded)
|
|
|
|
|
{
|
|
|
|
|
// Already loaded. The rest of the constant code will run right away.
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2024-08-13 10:23:49 -04:00
|
|
|
Issued = MakeShared<FLoadExtensionDataTask>(item, program.m_constantExtensionData[Args.value].Data, Args.value);
|
2023-04-05 11:20:44 -04:00
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
2022-09-26 15:12:13 -04:00
|
|
|
|
|
|
|
|
case OP_TYPE::IM_CONSTANT:
|
|
|
|
|
{
|
2024-08-13 10:23:49 -04:00
|
|
|
OP::ResourceConstantArgs args = program.GetOpArgs<OP::ResourceConstantArgs>(item.At);
|
|
|
|
|
int32 MipsToSkip = item.ExecutionOptions;
|
2022-09-26 15:12:13 -04:00
|
|
|
int32 ImageIndex = args.value;
|
2024-08-13 10:23:49 -04:00
|
|
|
int32 ReallySkip = FMath::Min(MipsToSkip, program.ConstantImages[ImageIndex].LODCount - 1);
|
|
|
|
|
int32 LODIndexIndex = program.ConstantImages[ImageIndex].FirstIndex + ReallySkip;
|
|
|
|
|
int32 LODIndexCount = program.ConstantImages[ImageIndex].LODCount - ReallySkip;
|
2022-09-26 15:12:13 -04:00
|
|
|
check(LODIndexCount > 0);
|
|
|
|
|
|
2023-06-20 04:26:21 -04:00
|
|
|
// We always need to follow this path, or roms may not be protected for long enough and might be unloaded
|
|
|
|
|
// because of memory budget contraints.
|
|
|
|
|
bool bAnyMissing = true;
|
|
|
|
|
//bool bAnyMissing = false;
|
|
|
|
|
//for (int32 i=0; i<LODIndexCount; ++i)
|
|
|
|
|
//{
|
2024-08-13 10:23:49 -04:00
|
|
|
// uint32 LODIndex = program.ConstantImageLODIndices[LODIndexIndex+i];
|
|
|
|
|
// if ( !program.ConstantImageLODs[LODIndex].Value )
|
2023-06-20 04:26:21 -04:00
|
|
|
// {
|
|
|
|
|
// bAnyMissing = true;
|
|
|
|
|
// break;
|
|
|
|
|
// }
|
|
|
|
|
//}
|
2022-09-26 15:12:13 -04:00
|
|
|
|
|
|
|
|
if (bAnyMissing)
|
|
|
|
|
{
|
2024-08-13 10:23:49 -04:00
|
|
|
Issued = MakeShared<FLoadImageRomsTask>(item, LODIndexIndex, LODIndexCount);
|
2022-09-26 15:12:13 -04:00
|
|
|
|
|
|
|
|
if (DebugRom && (DebugRomAll || ImageIndex == DebugImageIndex))
|
|
|
|
|
UE_LOG(LogMutableCore, Log, TEXT("Issuing image %d skipping %d ."), ImageIndex, ReallySkip);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
// If already available, the rest of the constant code will run right away.
|
|
|
|
|
if (DebugRom && (DebugRomAll || ImageIndex == DebugImageIndex))
|
|
|
|
|
UE_LOG(LogMutableCore, Log, TEXT("Image %d skipping %d is already loaded."), ImageIndex, ReallySkip);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2023-01-13 10:48:15 -05:00
|
|
|
case OP_TYPE::IM_PARAMETER:
|
|
|
|
|
{
|
2024-08-13 10:23:49 -04:00
|
|
|
OP::ParameterArgs args = program.GetOpArgs<OP::ParameterArgs>(item.At);
|
|
|
|
|
Ptr<RangeIndex> Index = BuildCurrentOpRangeIndex(item, m_pParams, m_pModel.Get(), args.variable);
|
2023-01-13 10:48:15 -05:00
|
|
|
|
2023-07-31 03:24:06 -04:00
|
|
|
const FName Id = m_pParams->GetImageValue(args.variable, Index);
|
2023-01-27 14:54:49 -05:00
|
|
|
|
|
|
|
|
check(ImageLOD < TNumericLimits<uint8>::Max() && ImageLOD >= 0);
|
2024-08-13 10:23:49 -04:00
|
|
|
check(ImageLOD + static_cast<int32>(item.ExecutionOptions) < TNumericLimits<uint8>::Max());
|
2023-01-27 14:54:49 -05:00
|
|
|
|
2024-08-13 10:23:49 -04:00
|
|
|
const uint8 MipmapsToSkip = item.ExecutionOptions + static_cast<uint8>(ImageLOD);
|
2023-01-27 14:54:49 -05:00
|
|
|
|
2024-05-14 07:28:21 -04:00
|
|
|
CodeRunner::FExternalResourceId FullId;
|
2023-10-05 04:46:46 -04:00
|
|
|
FullId.ParameterId = Id;
|
2024-08-13 10:23:49 -04:00
|
|
|
Issued = MakeShared<FImageExternalLoadTask>(item, MipmapsToSkip, FullId);
|
2023-01-13 10:48:15 -05:00
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
}
|
2023-10-05 04:46:46 -04:00
|
|
|
|
|
|
|
|
case OP_TYPE::IM_REFERENCE:
|
|
|
|
|
{
|
2024-08-13 10:23:49 -04:00
|
|
|
OP::ResourceReferenceArgs Args = m_pModel->GetPrivate()->m_program.GetOpArgs<OP::ResourceReferenceArgs>(item.At);
|
2023-10-05 04:46:46 -04:00
|
|
|
|
|
|
|
|
// We only convert references to images if indicated in the operation.
|
|
|
|
|
if (Args.ForceLoad)
|
|
|
|
|
{
|
2024-08-13 10:23:49 -04:00
|
|
|
check(item.Stage==0);
|
2023-10-05 04:46:46 -04:00
|
|
|
|
2024-08-13 10:23:49 -04:00
|
|
|
const uint8 MipmapsToSkip = item.ExecutionOptions + static_cast<uint8>(ImageLOD);
|
2023-10-05 04:46:46 -04:00
|
|
|
|
2024-05-14 07:28:21 -04:00
|
|
|
FExternalResourceId FullId;
|
|
|
|
|
FullId.ReferenceResourceId = Args.ID;
|
2024-08-13 10:23:49 -04:00
|
|
|
Issued = MakeShared<FImageExternalLoadTask>(item, MipmapsToSkip, FullId);
|
2023-10-05 04:46:46 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2024-05-14 07:28:21 -04:00
|
|
|
case OP_TYPE::ME_REFERENCE:
|
|
|
|
|
{
|
2024-08-13 10:23:49 -04:00
|
|
|
OP::ResourceReferenceArgs Args = m_pModel->GetPrivate()->m_program.GetOpArgs<OP::ResourceReferenceArgs>(item.At);
|
2024-05-14 07:28:21 -04:00
|
|
|
|
|
|
|
|
// We only convert references to meshes if indicated in the operation.
|
|
|
|
|
if (Args.ForceLoad)
|
|
|
|
|
{
|
2024-08-13 10:23:49 -04:00
|
|
|
check(item.Stage == 0);
|
2024-05-14 07:28:21 -04:00
|
|
|
|
|
|
|
|
FExternalResourceId FullId;
|
|
|
|
|
FullId.ReferenceResourceId = Args.ID;
|
2024-08-13 10:23:49 -04:00
|
|
|
Issued = MakeShared<FMeshExternalLoadTask>(item, FullId);
|
2024-05-14 07:28:21 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2022-09-26 15:12:13 -04:00
|
|
|
case OP_TYPE::IM_PIXELFORMAT:
|
|
|
|
|
{
|
2024-08-13 10:23:49 -04:00
|
|
|
if (item.Stage == 1)
|
2022-09-26 15:12:13 -04:00
|
|
|
{
|
2024-08-13 10:23:49 -04:00
|
|
|
OP::ImagePixelFormatArgs Args = program.GetOpArgs<OP::ImagePixelFormatArgs>(item.At);
|
|
|
|
|
Issued = MakeShared<FImagePixelFormatTask>(item, Args);
|
2022-09-26 15:12:13 -04:00
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case OP_TYPE::IM_LAYERCOLOUR:
|
|
|
|
|
{
|
2024-08-13 10:23:49 -04:00
|
|
|
if (item.Stage == 1)
|
2022-09-26 15:12:13 -04:00
|
|
|
{
|
2024-08-13 10:23:49 -04:00
|
|
|
OP::ImageLayerColourArgs Args = program.GetOpArgs<OP::ImageLayerColourArgs>(item.At);
|
|
|
|
|
Issued = MakeShared<FImageLayerColourTask>(item, Args);
|
2022-09-26 15:12:13 -04:00
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case OP_TYPE::IM_LAYER:
|
|
|
|
|
{
|
2024-08-13 10:23:49 -04:00
|
|
|
if ((ExecutionStrategy == EExecutionStrategy::MinimizeMemory && item.Stage == 2)
|
2023-03-08 04:37:54 -05:00
|
|
|
||
|
2024-08-13 10:23:49 -04:00
|
|
|
(ExecutionStrategy != EExecutionStrategy::MinimizeMemory && item.Stage == 1)
|
2023-03-08 04:37:54 -05:00
|
|
|
)
|
2022-09-26 15:12:13 -04:00
|
|
|
{
|
2024-08-13 10:23:49 -04:00
|
|
|
OP::ImageLayerArgs Args = program.GetOpArgs<OP::ImageLayerArgs>(item.At);
|
|
|
|
|
Issued = MakeShared<FImageLayerTask>(item, Args);
|
2022-09-26 15:12:13 -04:00
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case OP_TYPE::IM_MIPMAP:
|
|
|
|
|
{
|
2024-08-13 10:23:49 -04:00
|
|
|
if (item.Stage == 1)
|
2022-09-26 15:12:13 -04:00
|
|
|
{
|
2024-08-13 10:23:49 -04:00
|
|
|
OP::ImageMipmapArgs Args = program.GetOpArgs<OP::ImageMipmapArgs>(item.At);
|
|
|
|
|
Issued = MakeShared<FImageMipmapTask>(item, Args);
|
2022-09-26 15:12:13 -04:00
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case OP_TYPE::IM_SWIZZLE:
|
|
|
|
|
{
|
2024-08-13 10:23:49 -04:00
|
|
|
if (item.Stage == 1)
|
2022-09-26 15:12:13 -04:00
|
|
|
{
|
2024-08-13 10:23:49 -04:00
|
|
|
OP::ImageSwizzleArgs Args = program.GetOpArgs<OP::ImageSwizzleArgs>(item.At);
|
|
|
|
|
Issued = MakeShared<FImageSwizzleTask>(item, Args);
|
2022-09-26 15:12:13 -04:00
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2023-01-26 05:27:42 -05:00
|
|
|
case OP_TYPE::IM_SATURATE:
|
|
|
|
|
{
|
2024-08-13 10:23:49 -04:00
|
|
|
if (item.Stage == 1)
|
2023-01-26 05:27:42 -05:00
|
|
|
{
|
2024-08-13 10:23:49 -04:00
|
|
|
OP::ImageSaturateArgs Args = program.GetOpArgs<OP::ImageSaturateArgs>(item.At);
|
|
|
|
|
Issued = MakeShared<FImageSaturateTask>(item, Args);
|
2023-01-26 05:27:42 -05:00
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case OP_TYPE::IM_INVERT:
|
|
|
|
|
{
|
2024-08-13 10:23:49 -04:00
|
|
|
if (item.Stage == 1)
|
2023-01-26 05:27:42 -05:00
|
|
|
{
|
2024-08-13 10:23:49 -04:00
|
|
|
OP::ImageInvertArgs Args = program.GetOpArgs<OP::ImageInvertArgs>(item.At);
|
|
|
|
|
Issued = MakeShared<FImageInvertTask>(item, Args);
|
2023-01-26 05:27:42 -05:00
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case OP_TYPE::IM_RESIZE:
|
|
|
|
|
{
|
2024-08-13 10:23:49 -04:00
|
|
|
if (item.Stage == 1)
|
2023-01-26 05:27:42 -05:00
|
|
|
{
|
2024-08-13 10:23:49 -04:00
|
|
|
OP::ImageResizeArgs Args = program.GetOpArgs<OP::ImageResizeArgs>(item.At);
|
|
|
|
|
Issued = MakeShared<FImageResizeTask>(item, Args);
|
2023-01-26 05:27:42 -05:00
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case OP_TYPE::IM_RESIZEREL:
|
|
|
|
|
{
|
2024-08-13 10:23:49 -04:00
|
|
|
if (item.Stage == 1)
|
2023-01-26 05:27:42 -05:00
|
|
|
{
|
2024-08-13 10:23:49 -04:00
|
|
|
OP::ImageResizeRelArgs Args = program.GetOpArgs<OP::ImageResizeRelArgs>(item.At);
|
|
|
|
|
Issued = MakeShared<FImageResizeRelTask>(item, Args);
|
2023-01-26 05:27:42 -05:00
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2022-09-26 15:12:13 -04:00
|
|
|
case OP_TYPE::IM_COMPOSE:
|
|
|
|
|
{
|
2024-08-13 10:23:49 -04:00
|
|
|
if ((ExecutionStrategy == EExecutionStrategy::MinimizeMemory && item.Stage == 3) ||
|
|
|
|
|
(ExecutionStrategy != EExecutionStrategy::MinimizeMemory && item.Stage == 2))
|
2022-09-26 15:12:13 -04:00
|
|
|
{
|
2024-08-13 10:23:49 -04:00
|
|
|
OP::ImageComposeArgs Args = program.GetOpArgs<OP::ImageComposeArgs>(item.At);
|
|
|
|
|
Ptr<const Layout> ComposeLayout = static_cast<const Layout*>( m_heapData[item.CustomState].Resource.get());
|
|
|
|
|
Issued = MakeShared<FImageComposeTask>(item, Args, ComposeLayout);
|
2022-09-26 15:12:13 -04:00
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return Issued;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} // namespace mu
|