Files
UnrealEngine/Engine/Source/Runtime/Renderer/Private/VT/VirtualTextureProducer.cpp
2025-05-18 13:04:45 +08:00

290 lines
9.6 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "VirtualTextureProducer.h"
#include "VT/VirtualTexturePhysicalSpace.h"
#include "VT/VirtualTextureSystem.h"
FVirtualTextureProducer::~FVirtualTextureProducer()
{
}
void FVirtualTextureProducer::Release(FVirtualTextureSystem* System, const FVirtualTextureProducerHandle& HandleToSelf)
{
TRACE_CPUPROFILER_EVENT_SCOPE(FVirtualTextureProducer::Release);
if (Description.bPersistentHighestMip)
{
System->ForceUnlockAllTiles(HandleToSelf, this);
}
for (uint32 LayerIndex = 0u; LayerIndex < Description.NumPhysicalGroups; ++LayerIndex)
{
FVirtualTexturePhysicalSpace* Space = PhysicalGroups[LayerIndex].PhysicalSpace;
Space->GetPagePool().EvictPages(System, HandleToSelf);
PhysicalGroups[LayerIndex].PhysicalSpace.SafeRelease();
}
PhysicalGroups.Reset();
FGraphEventArray ProducePageTasks;
VirtualTexture->GatherProducePageDataTasks(HandleToSelf, ProducePageTasks);
if (ProducePageTasks.Num())
{
TRACE_CPUPROFILER_EVENT_SCOPE(FVirtualTextureProducer::Release_Wait);
FTaskGraphInterface::Get().WaitUntilTasksComplete(ProducePageTasks, ENamedThreads::GetRenderThread_Local());
}
delete VirtualTexture;
VirtualTexture = nullptr;
Description = FVTProducerDescription();
}
FVirtualTextureProducerCollection::FVirtualTextureProducerCollection() : NumPendingCallbacks(0u)
{
Producers.AddDefaulted(1);
Producers[0].Magic = 1u; // make sure FVirtualTextureProducerHandle(0) will not resolve to the dummy producer entry
Callbacks.AddDefaulted(CallbackList_Count);
for (uint32 CallbackIndex = 0u; CallbackIndex < CallbackList_Count; ++CallbackIndex)
{
FCallbackEntry& Callback = Callbacks[CallbackIndex];
Callback.NextIndex = Callback.PrevIndex = CallbackIndex;
}
}
FVirtualTextureProducerHandle FVirtualTextureProducerCollection::RegisterProducer(FRHICommandListBase& RHICmdList, FVirtualTextureSystem* System, const FVTProducerDescription& InDesc, IVirtualTexture* InProducer)
{
const uint32 ProducerWidth = InDesc.BlockWidthInTiles * InDesc.WidthInBlocks * InDesc.TileSize;
const uint32 ProducerHeight = InDesc.BlockHeightInTiles * InDesc.HeightInBlocks * InDesc.TileSize;
check(ProducerWidth > 0u);
check(ProducerHeight > 0u);
check(InDesc.MaxLevel <= FMath::CeilLogTwo(FMath::Max(ProducerWidth, ProducerHeight)));
check(InDesc.NumTextureLayers <= VIRTUALTEXTURE_SPACE_MAXLAYERS);
check(InDesc.NumPhysicalGroups <= InDesc.NumTextureLayers);
check(InProducer);
const uint32 Index = AcquireEntry();
FProducerEntry& Entry = Producers[Index];
Entry.Producer.Description = InDesc;
Entry.Producer.VirtualTexture = InProducer;
Entry.DestroyedCallbacksIndex = AcquireCallback();
FVTPhysicalSpaceDescription PhysicalSpaceDesc;
PhysicalSpaceDesc.Dimensions = InDesc.Dimensions;
PhysicalSpaceDesc.TileSize = InDesc.TileSize + InDesc.TileBorderSize * 2u;
PhysicalSpaceDesc.bCanSplit = !InDesc.bRequiresSinglePhysicalPool;
Entry.Producer.PhysicalGroups.SetNum(InDesc.NumPhysicalGroups);
for (uint32 PhysicalGroupIndex = 0u; PhysicalGroupIndex < InDesc.NumPhysicalGroups; ++PhysicalGroupIndex)
{
PhysicalSpaceDesc.NumLayers = 0;
for (uint32 LayerIndex = 0u; LayerIndex < InDesc.NumTextureLayers; ++LayerIndex)
{
if (InDesc.PhysicalGroupIndex[LayerIndex] == PhysicalGroupIndex)
{
PhysicalSpaceDesc.Format[PhysicalSpaceDesc.NumLayers] = InDesc.LayerFormat[LayerIndex];
// sRGB on/off flag is ignored on platforms that has support for texture aliasing
PhysicalSpaceDesc.bHasLayerSrgbView[PhysicalSpaceDesc.NumLayers] = (GRHISupportsTextureViews ? true : InDesc.bIsLayerSRGB[LayerIndex]);
PhysicalSpaceDesc.NumLayers++;
}
}
check(PhysicalSpaceDesc.NumLayers > 0);
Entry.Producer.PhysicalGroups[PhysicalGroupIndex].PhysicalSpace = System->AcquirePhysicalSpace(RHICmdList, PhysicalSpaceDesc);
}
const FVirtualTextureProducerHandle Handle(Index, Entry.Magic);
return Handle;
}
void FVirtualTextureProducerCollection::ReleaseProducer(FVirtualTextureSystem* System, const FVirtualTextureProducerHandle& Handle)
{
if (FProducerEntry* Entry = GetEntry(Handle))
{
uint32 CallbackIndex = Callbacks[Entry->DestroyedCallbacksIndex].NextIndex;
while (CallbackIndex != Entry->DestroyedCallbacksIndex)
{
FCallbackEntry& Callback = Callbacks[CallbackIndex];
const uint32 NextIndex = Callback.NextIndex;
check(Callback.OwnerHandle == Handle);
check(!Callback.bPending);
Callback.bPending = true;
// Move callback to pending list
RemoveCallbackFromList(CallbackIndex);
AddCallbackToList(CallbackList_Pending, CallbackIndex);
++NumPendingCallbacks;
CallbackIndex = NextIndex;
}
ReleaseCallback(Entry->DestroyedCallbacksIndex);
Entry->DestroyedCallbacksIndex = 0u;
Entry->Magic = (Entry->Magic + 1u) & 1023u;
Entry->Producer.Release(System, Handle);
ReleaseEntry(Handle.Index);
}
}
bool FVirtualTextureProducerCollection::TryReleaseProducer(FVirtualTextureSystem* System, const FVirtualTextureProducerHandle& Handle)
{
if (FProducerEntry* Entry = GetEntry(Handle))
{
FGraphEventArray ProducePageTasks;
Entry->Producer.GetVirtualTexture()->GatherProducePageDataTasks(Handle, ProducePageTasks);
if (ProducePageTasks.Num() != 0)
{
// Don't release with tasks remaining.
return false;
}
}
ReleaseProducer(System, Handle);
return true;
}
void FVirtualTextureProducerCollection::CallPendingCallbacks()
{
uint32 CallbackIndex = Callbacks[CallbackList_Pending].NextIndex;
uint32 NumCallbacksChecked = 0u;
while (CallbackIndex != CallbackList_Pending)
{
FCallbackEntry& Callback = Callbacks[CallbackIndex];
check(Callback.bPending);
// Make a copy, then release the callback entry before calling the callback function
// (The destroyed callback may try to remove this or other callbacks, so need to make sure state is valid before calling)
const FCallbackEntry CallbackCopy(Callback);
if (Callback.Baton)
{
TArray<uint32>& BatonCallbacks = CallbacksMap.FindChecked(Callback.Baton);
BatonCallbacks.Remove(CallbackIndex);
if (BatonCallbacks.IsEmpty())
{
CallbacksMap.Remove(Callback.Baton);
}
}
Callback.DestroyedFunction = nullptr;
Callback.Baton = nullptr;
Callback.OwnerHandle = FVirtualTextureProducerHandle();
Callback.PackedFlags = 0u;
ReleaseCallback(CallbackIndex);
// Possible that this callback may have been removed from the list by a previous pending callback
// In this case, the function pointer will be set to nullptr
if (CallbackCopy.DestroyedFunction)
{
check(CallbackCopy.OwnerHandle.PackedValue != 0u);
CallbackCopy.DestroyedFunction(CallbackCopy.OwnerHandle, CallbackCopy.Baton);
}
CallbackIndex = CallbackCopy.NextIndex;
++NumCallbacksChecked;
}
// Extra check to detect list corruption
check(NumCallbacksChecked == NumPendingCallbacks);
NumPendingCallbacks = 0u;
}
bool FVirtualTextureProducerCollection::HasPendingCallbacks() const
{
return NumPendingCallbacks != 0;
}
void FVirtualTextureProducerCollection::AddDestroyedCallback(const FVirtualTextureProducerHandle& Handle, FVTProducerDestroyedFunction* Function, void* Baton)
{
check(Function);
check(Baton);
FProducerEntry* Entry = GetEntry(Handle);
if (Entry)
{
const uint32 CallbackIndex = AcquireCallback();
AddCallbackToList(Entry->DestroyedCallbacksIndex, CallbackIndex);
FCallbackEntry& Callback = Callbacks[CallbackIndex];
Callback.DestroyedFunction = Function;
Callback.Baton = Baton;
Callback.OwnerHandle = Handle;
Callback.PackedFlags = 0u;
CallbacksMap.FindOrAdd(Baton).Add(CallbackIndex);
}
}
uint32 FVirtualTextureProducerCollection::RemoveAllCallbacks(const void* Baton)
{
check(Baton);
uint32 NumRemoved = 0u;
TArray<uint32>* CallbackIndices = CallbacksMap.Find(Baton);
if (CallbackIndices != nullptr)
{
for (int32 CallbackIndex : *CallbackIndices)
{
FCallbackEntry& Callback = Callbacks[CallbackIndex];
check(Callback.Baton == Baton);
check(Callback.DestroyedFunction);
Callback.DestroyedFunction = nullptr;
Callback.Baton = nullptr;
Callback.OwnerHandle = FVirtualTextureProducerHandle();
// If callback is already pending, we can't move it back to free list, or we risk corrupting the pending list while it's being iterated
// Setting DestroyedFunction to nullptr here will ensure callback is no longer invoked, and it will be moved to free list later when it's removed from pending list
if (!Callback.bPending)
{
Callback.PackedFlags = 0u;
ReleaseCallback(CallbackIndex);
}
++NumRemoved;
}
CallbacksMap.Remove(Baton);
}
return NumRemoved;
}
void FVirtualTextureProducerCollection::NotifyRequestsCompleted()
{
TRACE_CPUPROFILER_EVENT_SCOPE(FVirtualTextureSystem::NotifyRequestsComplete);
for (FProducerEntry& Entry : Producers)
{
if (Entry.Producer.Description.bNotifyCompleted)
{
Entry.Producer.GetVirtualTexture()->OnRequestsCompleted();
}
}
}
FVirtualTextureProducer* FVirtualTextureProducerCollection::FindProducer(const FVirtualTextureProducerHandle& Handle)
{
FProducerEntry* Entry = GetEntry(Handle);
return Entry ? &Entry->Producer : nullptr;
}
FVirtualTextureProducer& FVirtualTextureProducerCollection::GetProducer(const FVirtualTextureProducerHandle& Handle)
{
const uint32 Index = Handle.Index;
check(Index < (uint32)Producers.Num());
FProducerEntry& Entry = Producers[Index];
check(Entry.Magic == Handle.Magic);
return Entry.Producer;
}
FVirtualTextureProducerCollection::FProducerEntry* FVirtualTextureProducerCollection::GetEntry(const FVirtualTextureProducerHandle& Handle)
{
const uint32 Index = Handle.Index;
if (Index < (uint32)Producers.Num())
{
FProducerEntry& Entry = Producers[Index];
if (Entry.Magic == Handle.Magic)
{
return &Entry;
}
}
return nullptr;
}