Files

240 lines
8.7 KiB
C++

// Copyright Ngonart OU. All Rights Reserved.
// UE5 Modular Game Framework — SS_PlanarCaptureManager (138)
// Global budget manager for all planar capture surfaces in the world.
//
// One instance per World (World Subsystem). Each frame, scores every registered
// capture surface by distance, screen coverage, facing angle, and scripted priority.
// Assigns quality tiers across all surfaces respecting a global budget
// (max simultaneous high-quality captures, max total render target memory).
// Forces idle/disabled state on surfaces outside active rooms/sublevels.
//
// Also manages the render target pool — allocates, reuses, and resizes RTs
// to minimize memory churn.
#pragma once
#include "CoreMinimal.h"
#include "Subsystems/WorldSubsystem.h"
#include "Tickable.h"
#include "Misc/Optional.h"
#include "Capture/PlanarCaptureCommon.h"
#include "SS_PlanarCaptureManager.generated.h"
class ABP_PlanarCaptureActor;
class UBPC_PlanarCapture;
// Delegates
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnSurfaceRegistered, ABP_PlanarCaptureActor*, Surface, int32, TotalSurfaces);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnSurfaceUnregistered, ABP_PlanarCaptureActor*, Surface, int32, TotalSurfaces);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnGlobalQualityCapChanged, EPlanarCaptureQualityTier, NewCap);
/**
* SS_PlanarCaptureManager — Global Planar Capture Budget Manager.
*
* Manages all ABP_PlanarCaptureActor instances per world. Evaluates priority
* and assigns quality tiers to stay within a configurable budget.
* Also owns the render target pool to reduce memory allocation overhead.
*
* Multiplayer: This subsystem exists on both server and clients.
* On the server, it tracks surfaces for replication state.
* On clients, it drives actual capture rendering.
*/
UCLASS()
class PG_FRAMEWORK_API USS_PlanarCaptureManager : public UWorldSubsystem, public FTickableGameObject
{
GENERATED_BODY()
public:
USS_PlanarCaptureManager();
// ========================================================================
// Lifecycle
// ========================================================================
virtual void Initialize(FSubsystemCollectionBase& Collection) override;
virtual void Deinitialize() override;
// FTickableGameObject
virtual void Tick(float DeltaTime) override;
virtual TStatId GetStatId() const override;
virtual bool IsTickable() const override { return !IsTemplate(); }
virtual bool IsTickableInEditor() const override { return false; }
virtual UWorld* GetTickableGameObjectWorld() const override { return GetWorld(); }
// ========================================================================
// Surface Registry
// ========================================================================
/**
* Register a capture surface actor with the global manager.
* Called by ABP_PlanarCaptureActor::BeginPlay.
*/
UFUNCTION(BlueprintCallable, Category = "Capture|Manager")
void RegisterSurface(ABP_PlanarCaptureActor* Surface);
/**
* Unregister a capture surface actor. Called on EndPlay.
*/
UFUNCTION(BlueprintCallable, Category = "Capture|Manager")
void UnregisterSurface(ABP_PlanarCaptureActor* Surface);
/**
* Get all currently registered surfaces.
*/
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Capture|Manager")
TArray<ABP_PlanarCaptureActor*> GetRegisteredSurfaces() const;
/**
* Get the number of registered surfaces.
*/
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Capture|Manager")
int32 GetSurfaceCount() const { return RegisteredSurfaces.Num(); }
// ========================================================================
// Quality Budget Management
// ========================================================================
/**
* Global quality ceiling — caps all surfaces at this tier regardless of score.
* Use this for lower-end hardware targets.
*/
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Capture|Manager|Budget")
EPlanarCaptureQualityTier GlobalQualityCap = EPlanarCaptureQualityTier::High;
/**
* Maximum number of simultaneous Hero-tier captures.
*/
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Capture|Manager|Budget")
int32 MaxHeroSurfaces = 1;
/**
* Maximum number of simultaneous High-tier captures.
*/
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Capture|Manager|Budget")
int32 MaxHighSurfaces = 3;
/**
* Maximum number of simultaneous Medium-tier captures.
*/
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Capture|Manager|Budget")
int32 MaxMediumSurfaces = 6;
/**
* Maximum total render target memory in megabytes across all surfaces.
*/
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Capture|Manager|Budget")
float MaxTotalRenderTargetMemoryMB = 128.0f;
/**
* Distance at which capture quality drops to Off (world units).
*/
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Capture|Manager|Budget")
float MaxCaptureDistance = 10000.0f;
/**
* Interval between full re-evaluation of all surfaces (seconds).
* Individual surfaces check their own interval every frame via their component.
*/
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Capture|Manager|Budget")
float FullEvaluationInterval = 0.5f;
// ========================================================================
// Render Target Pool
// ========================================================================
/**
* Request a render target from the pool. Returns nullptr if none available.
*/
UTextureRenderTarget2D* RequestRenderTarget(int32 Size);
/**
* Release a render target back to the pool.
*/
void ReleaseRenderTarget(UTextureRenderTarget2D* RenderTarget);
/**
* Get the total memory used by the render target pool (in MB).
*/
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Capture|Manager")
float GetPoolMemoryUsageMB() const;
// ========================================================================
// Query
// ========================================================================
/**
* Get the nearest capture surface of a given mode to a world location.
*/
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Capture|Manager")
ABP_PlanarCaptureActor* GetNearestSurfaceOfMode(
EPlanarCaptureMode Mode, FVector WorldLocation, float MaxDistance = 0.0f) const;
/**
* Force all surfaces to a specific quality tier (e.g., for cutscenes).
*/
UFUNCTION(BlueprintCallable, Category = "Capture|Manager")
void ForceAllSurfacesToTier(EPlanarCaptureQualityTier Tier);
/**
* Release the force-tier override and resume normal scoring.
*/
UFUNCTION(BlueprintCallable, Category = "Capture|Manager")
void ReleaseForceTier();
// ========================================================================
// Event Dispatchers
// ========================================================================
UPROPERTY(BlueprintAssignable, Category = "Capture|Manager|Events")
FOnSurfaceRegistered OnSurfaceRegistered;
UPROPERTY(BlueprintAssignable, Category = "Capture|Manager|Events")
FOnSurfaceUnregistered OnSurfaceUnregistered;
UPROPERTY(BlueprintAssignable, Category = "Capture|Manager|Events")
FOnGlobalQualityCapChanged OnGlobalQualityCapChanged;
protected:
// ========================================================================
// Internal State
// ========================================================================
/** All registered capture surface actors. */
UPROPERTY()
TArray<TWeakObjectPtr<ABP_PlanarCaptureActor>> RegisteredSurfaces;
/** Render target pool. */
TArray<FPlanarCaptureRenderTargetEntry> RenderTargetPool;
/** Time accumulator for full re-evaluation interval. */
float TimeSinceLastEvaluation = 0.0f;
/** Force-tier override — if set, all surfaces use this tier. */
TOptional<EPlanarCaptureQualityTier> ForceTierOverride;
/** Count of surfaces at each tier (tracked for budget enforcement). */
TMap<EPlanarCaptureQualityTier, int32> TierAssignmentCounts;
// ========================================================================
// Internal Methods
// ========================================================================
/** Evaluate all registered surfaces and assign quality tiers. */
void EvaluateAllSurfaces();
/**
* Score a single surface and determine its quality tier within budget constraints.
* @return The assigned tier.
*/
EPlanarCaptureQualityTier ScoreAndAssignTier(UBPC_PlanarCapture* Capture);
/** Enforce budget limits — demote lower-priority surfaces if budget exceeded. */
void EnforceBudgetLimits();
/** Create a new render target of the given size. */
UTextureRenderTarget2D* CreateRenderTarget(int32 Size);
/** Get or create a render target for a given size (first checks pool). */
UTextureRenderTarget2D* GetOrCreateRenderTarget(int32 Size);
};