Files
UnrealEngine/Engine/Source/Runtime/Landscape/Classes/LandscapeTextureStorageProvider.h
2025-05-18 13:04:45 +08:00

255 lines
10 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "Engine/TextureMipDataProviderFactory.h"
#include "Streaming/TextureMipDataProvider.h"
#include "Containers/ContainersFwd.h"
#include "Algo/Accumulate.h"
#include "Serialization/BulkData.h"
#include "Engine/TextureAllMipDataProviderFactory.h"
#include "LandscapeEdgeFixup.h"
#include "LandscapeTextureStorageProvider.generated.h"
class ULandscapeTextureStorageProviderFactory;
class ULandscapeHeightmapTextureEdgeFixup;
class UTexture2D;
USTRUCT()
struct FLandscapeTexture2DMipMap
{
GENERATED_BODY()
/** Width of the mip-map. */
UPROPERTY()
int32 SizeX = 0;
/** Height of the mip-map. */
UPROPERTY()
int32 SizeY = 0;
/** Whether the bulk data is compressed or not */
UPROPERTY()
bool bCompressed = false;
FByteBulkData BulkData;
void Serialize(FArchive& Ar, UObject* Owner, uint32 SaveOverrideFlags);
};
class FLandscapeTextureStorageMipProvider : public FTextureMipDataProvider
{
public:
FLandscapeTextureStorageMipProvider(ULandscapeTextureStorageProviderFactory* InFactory);
~FLandscapeTextureStorageMipProvider();
// ********************************************************
// ********* FLandscapeTextureStorageMipProvider implementation **********
// ********************************************************
void Init(const FTextureUpdateContext& Context, const FTextureUpdateSyncOptions& SyncOptions) final override;
int32 GetMips(const FTextureUpdateContext& Context, int32 StartingMipIndex, const FTextureMipInfoArray& MipInfos, const FTextureUpdateSyncOptions& SyncOptions) final override;
bool PollMips(const FTextureUpdateSyncOptions& SyncOptions) final override;
void AbortPollMips() final override;
void CleanUp(const FTextureUpdateSyncOptions& SyncOptions) final override;
void Cancel(const FTextureUpdateSyncOptions& SyncOptions) final override;
ETickThread GetCancelThread() const final override;
private:
// the Factory (that actually has the mip data)
ULandscapeTextureStorageProviderFactory* Factory = nullptr;
// A structured with information about which file contains which mips.
struct FIORequest
{
FIoFilenameHash FilenameHash = INVALID_IO_FILENAME_HASH;
TUniquePtr<IBulkDataIORequest> BulkDataIORequest;
uint8* DestMipData = nullptr;
};
// Pending async requests created in GetMips().
TArray<FIORequest, TInlineAllocator<MAX_TEXTURE_MIP_COUNT>> IORequests;
// We make a copy of this, as it is provided in GetMips, and we fill it out in PollMips
FTextureMipInfoArray DestMipInfos;
int32 FirstRequestedMipIndex = INDEX_NONE;
// The asset name, used to log IO errors.
FName TextureName;
// Whether async read requests must be created with high priority (executes faster).
bool bPrioritizedIORequest = false;
// Whether async read requests where cancelled for any reasons.
bool bIORequestCancelled = false;
// Whether async read requests were required to abort through AbortPollMips().
bool bIORequestAborted = false;
// A callback to be executed once all IO pending requests are completed.
FBulkDataIORequestCallBack AsyncFileCallBack;
// Helper to create the AsyncFileCallBack -- which can handle IO Request completions (and will trigger the next step to be scheduled via SyncOptions.RescheduleCallback())
void CreateAsyncFileCallback(const FTextureUpdateSyncOptions& SyncOptions);
void ClearIORequests();
};
class FLandscapeTextureMipEdgeOverrideProvider : public FTextureMipDataProvider
{
// This class is actually a "Modifier" in that it doesn't provide any mips, it only modifies mips provided by the default providers
// It relies on some assumptions about the existing provider execution that are currently true, but may not remain true forever:
// 1) that all providers are executed, even if they are not actually handling any mips
// 2) that all default providers do NOT modify mip data in PollMips (this is true for both FTexture2DMipDataProvider_IO and FTexture2DMipDataProvider_DDC currently)
// this allows us to modify mip data in PollMips and not worry about being stomped on.
// IF either of these cease to be true, we should probably write a first class FTextureMipDataModifier path, and convert this to be a modifier instead of a provider.
public:
FLandscapeTextureMipEdgeOverrideProvider(ULandscapeHeightmapTextureEdgeFixup* InEdgeFixup, UTexture2D* InTexture);
~FLandscapeTextureMipEdgeOverrideProvider();
// ****************************************************************************
// ********* FLandscapeTextureMipEdgeOverrideProvider implementation **********
// ****************************************************************************
void Init(const FTextureUpdateContext& Context, const FTextureUpdateSyncOptions& SyncOptions) final override;
int32 GetMips(const FTextureUpdateContext& Context, int32 StartingMipIndex, const FTextureMipInfoArray& MipInfos, const FTextureUpdateSyncOptions& SyncOptions) final override;
bool PollMips(const FTextureUpdateSyncOptions& SyncOptions) final override;
void CleanUp(const FTextureUpdateSyncOptions& SyncOptions) final override;
void Cancel(const FTextureUpdateSyncOptions& SyncOptions) final override;
ETickThread GetCancelThread() const final override;
private:
// the Edge Fixup (that actually has the edge override data)
ULandscapeHeightmapTextureEdgeFixup* EdgeFixup = nullptr;
// We make a copy of this, as it is provided in GetMips, and we fill it out in PollMips
FTextureMipInfoArray DestMipInfos;
// The asset name, used to log IO errors.
FName TextureName;
};
// serves as a hook into the texture streaming process, allowing us to inject edge data
UCLASS(NotBlueprintable, MinimalAPI, Within = Texture2D)
class ULandscapeTextureMipEdgeOverrideFactory : public UTextureMipDataProviderFactory
{
GENERATED_UCLASS_BODY()
public:
TObjectPtr<UTexture2D> Texture; // should be the same as GetOuter()
TObjectPtr<ULandscapeHeightmapTextureEdgeFixup> EdgeFixup; // assigned when the EdgeFixup is Active
static ULandscapeTextureMipEdgeOverrideFactory* AddTo(UTexture2D* TargetTexture);
void SetupEdgeFixup(ULandscapeHeightmapTextureEdgeFixup* InEdgeFixup);
virtual void Serialize(FArchive& Ar) override;
static void AddReferencedObjects(UObject* InThis, FReferenceCollector& Collector);
/**
* Create a FTextureMipDataProvider to handle a single StreamIn mip operation.
* The object lifetime will be managed by FRenderAssetUpdate.
*
* @param InAsset - the texture on which the stream in operation will be performed.
*/
virtual FTextureMipDataProvider* AllocateMipDataProvider(UTexture* Asset) override
{
check(Asset == (UTexture*) Texture.Get());
return new FLandscapeTextureMipEdgeOverrideProvider(EdgeFixup, Texture);
}
/**
* Returns true if TextureMipDataProviders allocated by this factory can provide MipData by themselves,
* even without loading from disk at all, so streaming can be enabled for their textures.
*/
virtual bool WillProvideMipDataWithoutDisk() const
{
return true;
}
};
UCLASS(NotBlueprintable, MinimalAPI, Within = Texture2D)
class ULandscapeTextureStorageProviderFactory : public UTextureAllMipDataProviderFactory
{
GENERATED_UCLASS_BODY()
public:
int32 NumNonOptionalMips = 0;
int32 NumNonStreamingMips = 0;
FVector LandscapeGridScale = FVector(ForceInit);
TArray<FLandscapeTexture2DMipMap> Mips;
TObjectPtr<UTexture2D> Texture;
// Reference to the edge fixup on the Texture, if any. (This can be null, if edge fixup is disabled)
// It is set when the Texture's landscape component is registered.
TObjectPtr<ULandscapeHeightmapTextureEdgeFixup> EdgeFixup;
#if WITH_EDITORONLY_DATA
static ULandscapeTextureStorageProviderFactory* ApplyTo(UTexture2D* InTargetTexture, const FVector& InLandsapeGridScale, int32 InHeightmapCompressionMipThreshold);
void UpdateCompressedDataFromSource(UTexture2D* InTargetTexture, const FVector& InLandsapeGridScale, int32 InHeightmapCompressionMipThreshold);
#endif // WITH_EDITORONLY_DATA
void SetupEdgeFixup(ULandscapeHeightmapTextureEdgeFixup* EdgeFixup);
// compress the mip data.
static void CompressMipToBulkData(int32 MipIndex, int32 MipSizeX, int32 MipSizeY, uint8* SourceData, int32 SourceDataBytes, FByteBulkData& DestBulkData);
static void CopyMipToBulkData(int32 MipIndex, int32 MipSizeX, int32 MipSizeY, uint8* SourceData, int32 SourceDataBytes, FByteBulkData& DestBulkData);
// decompress the mip data. Currently operates in-place (though this could be changed in the future)
void DecompressMip(uint8* SourceData, int64 SourceDataBytes, uint8* DestData, int64 DestDataBytes, int32 MipIndex);
void DecompressMipOriginalUnoptimized(uint8* SourceData, int64 SourceDataBytes, uint8* DestData, int64 DestDataBytes, int32 MipIndex);
virtual void Serialize(FArchive& Ar) override;
static void AddReferencedObjects(UObject* InThis, FReferenceCollector& Collector);
/**
* Create a FTextureMipDataProvider to handle a single StreamIn mip operation.
* The object lifetime will be managed by FRenderAssetUpdate.
*
* @param InAsset - the texture on which the stream in operation will be performed.
*/
virtual FTextureMipDataProvider* AllocateMipDataProvider(UTexture* Asset) override
{
return new FLandscapeTextureStorageMipProvider(this);
}
/**
* Returns true if TextureMipDataProviders allocated by this factory can provide MipData by themselves,
* even without loading from disk at all, so streaming can be enabled for their textures.
*/
virtual bool WillProvideMipDataWithoutDisk() const
{
return true;
}
// load mip data (used to access preloaded inline mips for creating the initial version of the texture)
virtual bool GetInitialMipData(int32 FirstMipToLoad, TArrayView<void*> OutMipData, TArrayView<int64> OutMipSize, FStringView DebugContext) override;
// return the initial streaming state of the texture
virtual FStreamableRenderResourceState GetResourcePostInitState(const UTexture* Owner, bool bAllowStreaming) override;
bool DoesMipDataExist(int32 MipIndex)
{
// we need to check the bulk data exists (might not for optional mips)
return Mips.IsValidIndex(MipIndex) && Mips[MipIndex].BulkData.DoesExist();
}
const FLandscapeTexture2DMipMap* GetMip(int32 MipIndex)
{
int32 Index = MipIndex;
if (Mips.IsValidIndex(Index))
{
return &Mips[Index];
}
return nullptr;
}
int32 GetTotalBytes() const
{
return Algo::TransformAccumulate(Mips, [](const FLandscapeTexture2DMipMap& Mip) -> int32 { return Mip.BulkData.GetBulkDataSize(); }, 0);
}
};