Add dynamic textures streaming based on visibility

This commit is contained in:
Wojtek Figat
2021-06-18 14:41:44 +02:00
parent 41f0554484
commit f582ca5051
13 changed files with 70 additions and 39 deletions

View File

@@ -13,6 +13,7 @@ GPUContext::GPUContext(GPUDevice* device)
void GPUContext::FrameBegin()
{
_lastRenderTime = Platform::GetTimeSeconds();
}
void GPUContext::FrameEnd()

View File

@@ -121,18 +121,14 @@ private:
protected:
/// <summary>
/// Initializes a new instance of the <see cref="GPUContext"/> class.
/// </summary>
/// <param name="device">The graphics device.</param>
double _lastRenderTime = -1;
GPUContext(GPUDevice* device);
public:
/// <summary>
/// Gets the graphics device handle
/// Gets the graphics device.
/// </summary>
/// <returns>Graphics device</returns>
FORCE_INLINE GPUDevice* GetDevice() const
{
return _device;

View File

@@ -191,6 +191,8 @@ void GPUResource::OnDeleteObject()
PersistentScriptingObject::OnDeleteObject();
}
double GPUResourceView::DummyLastRenderTime = -1;
struct GPUDevice::PrivateData
{
AssetReference<Shader> QuadShader;

View File

@@ -65,6 +65,9 @@ public:
public:
// Points to the cache used by the resource for the resource visibility/usage detection. Written during rendering when resource is used.
double LastRenderTime = -1;
/// <summary>
/// Action fired when resource GPU state gets released. All objects and async tasks using this resource should release references to this object nor use its data.
/// </summary>
@@ -215,14 +218,19 @@ API_CLASS(Abstract, NoSpawn, Attributes="HideInEditor") class FLAXENGINE_API GPU
{
DECLARE_SCRIPTING_TYPE_NO_SPAWN(GPUResourceView);
protected:
static double DummyLastRenderTime;
explicit GPUResourceView(const SpawnParams& params)
: PersistentScriptingObject(params)
, LastRenderTime(&DummyLastRenderTime)
{
}
public:
// Points to the cache used by the resource for the resource visibility/usage detection. Written during rendering when resource view is used.
double* LastRenderTime;
/// <summary>
/// Gets the native pointer to the underlying view. It's a platform-specific handle.
/// </summary>

View File

@@ -31,11 +31,12 @@ protected:
{
}
void Init(GPUResource* parent, PixelFormat format, MSAALevel msaa)
FORCE_INLINE void Init(GPUResource* parent, PixelFormat format, MSAALevel msaa)
{
_parent = parent;
_format = format;
_msaa = msaa;
LastRenderTime = &parent->LastRenderTime;
}
public:

View File

@@ -327,26 +327,26 @@ void GPUContextDX11::BindCB(int32 slot, GPUConstantBuffer* cb)
void GPUContextDX11::BindSR(int32 slot, GPUResourceView* view)
{
ASSERT(slot >= 0 && slot < GPU_MAX_SR_BINDED);
auto handle = view ? ((IShaderResourceDX11*)view->GetNativePtr())->SRV() : nullptr;
if (_srHandles[slot] != handle)
{
_srDirtyFlag = true;
_srHandles[slot] = handle;
if (view)
*view->LastRenderTime = _lastRenderTime;
}
}
void GPUContextDX11::BindUA(int32 slot, GPUResourceView* view)
{
ASSERT(slot >= 0 && slot < GPU_MAX_UA_BINDED);
auto handle = view ? ((IShaderResourceDX11*)view->GetNativePtr())->UAV() : nullptr;
if (_uaHandles[slot] != handle)
{
_uaDirtyFlag = true;
_uaHandles[slot] = handle;
if (view)
*view->LastRenderTime = _lastRenderTime;
}
}

View File

@@ -809,9 +809,7 @@ void GPUContextDX12::ResetCB()
void GPUContextDX12::BindCB(int32 slot, GPUConstantBuffer* cb)
{
ASSERT(slot >= 0 && slot < GPU_MAX_CB_BINDED);
auto cbDX12 = static_cast<GPUConstantBufferDX12*>(cb);
if (_cbHandles[slot] != cbDX12)
{
_cbDirtyFlag = true;
@@ -828,6 +826,8 @@ void GPUContextDX12::BindSR(int32 slot, GPUResourceView* view)
_srMaskDirtyGraphics |= 1 << slot;
_srMaskDirtyCompute |= 1 << slot;
_srHandles[slot] = handle;
if (view)
*view->LastRenderTime = _lastRenderTime;
}
}
@@ -835,6 +835,8 @@ void GPUContextDX12::BindUA(int32 slot, GPUResourceView* view)
{
ASSERT(slot >= 0 && slot < GPU_MAX_UA_BINDED);
_uaHandles[slot] = view ? (IShaderResourceDX12*)view->GetNativePtr() : nullptr;
if (view)
*view->LastRenderTime = _lastRenderTime;
}
void GPUContextDX12::BindVB(const Span<GPUBuffer*>& vertexBuffers, const uint32* vertexBuffersOffsets)

View File

@@ -948,26 +948,26 @@ void GPUContextVulkan::BindCB(int32 slot, GPUConstantBuffer* cb)
void GPUContextVulkan::BindSR(int32 slot, GPUResourceView* view)
{
ASSERT(slot >= 0 && slot < GPU_MAX_SR_BINDED);
const auto handle = view ? (DescriptorOwnerResourceVulkan*)view->GetNativePtr() : nullptr;
if (_srHandles[slot] != handle)
{
_srDirtyFlag = true;
_srHandles[slot] = handle;
if (view)
*view->LastRenderTime = _lastRenderTime;
}
}
void GPUContextVulkan::BindUA(int32 slot, GPUResourceView* view)
{
ASSERT(slot >= 0 && slot < GPU_MAX_UA_BINDED);
const auto handle = view ? (DescriptorOwnerResourceVulkan*)view->GetNativePtr() : nullptr;
if (_uaHandles[slot] != handle)
{
_uaDirtyFlag = true;
_uaHandles[slot] = handle;
if (view)
*view->LastRenderTime = _lastRenderTime;
}
}

View File

@@ -22,9 +22,10 @@ public:
/// Calculates target quality level (0-1) for the given resource.
/// </summary>
/// <param name="resource">The resource.</param>
/// <param name="now">The current time.</param>
/// <param name="now">The current time and date.</param>
/// <param name="currentTime">The current platform time (seconds).</param>
/// <returns>Target quality (0-1).</returns>
virtual float CalculateTargetQuality(StreamableResource* resource, DateTime now) = 0;
virtual float CalculateTargetQuality(StreamableResource* resource, DateTime now, double currentTime) = 0;
/// <summary>
/// Calculates the residency level for a given resource and quality level.

View File

@@ -84,7 +84,7 @@ void StreamableResource::StopStreaming()
}
}
void UpdateResource(StreamableResource* resource, DateTime now)
void UpdateResource(StreamableResource* resource, DateTime now, double currentTime)
{
ASSERT(resource && resource->CanBeUpdated());
@@ -96,7 +96,7 @@ void UpdateResource(StreamableResource* resource, DateTime now)
float targetQuality = 1.0f;
if (resource->IsDynamic())
{
targetQuality = handler->CalculateTargetQuality(resource, now);
targetQuality = handler->CalculateTargetQuality(resource, now, currentTime);
targetQuality = Math::Saturate(targetQuality);
}
@@ -175,7 +175,7 @@ void StreamingManagerService::Update()
// Configuration
// TODO: use game settings
static TimeSpan ManagerUpdatesInterval = TimeSpan::FromMilliseconds(30);
static TimeSpan ResourceUpdatesInterval = TimeSpan::FromMilliseconds(200);
static TimeSpan ResourceUpdatesInterval = TimeSpan::FromMilliseconds(100);
static int32 MaxResourcesPerUpdate = 50;
// Check if skip update
@@ -191,6 +191,7 @@ void StreamingManagerService::Update()
// Start update
int32 resourcesUpdates = Math::Min(MaxResourcesPerUpdate, resourcesCount);
double currentTime = Platform::GetTimeSeconds();
// Update high priority queue and then rest of the resources
// Note: resources in the update queue are updated always, while others only between specified intervals
@@ -208,7 +209,7 @@ void StreamingManagerService::Update()
// Try to update it
if (now - resource->Streaming.LastUpdate >= ResourceUpdatesInterval && resource->CanBeUpdated())
{
UpdateResource(resource, now);
UpdateResource(resource, now, currentTime);
resourcesUpdates--;
}
}

View File

@@ -4,26 +4,33 @@
#include "Streaming.h"
#include "Engine/Core/Math/Math.h"
#include "Engine/Graphics/Textures/StreamingTexture.h"
#include "Engine/Graphics/Textures/GPUTexture.h"
#include "Engine/Content/Assets/Model.h"
#include "Engine/Content/Assets/SkinnedModel.h"
#include "Engine/Audio/AudioClip.h"
#include "Engine/Audio/Audio.h"
#include "Engine/Audio/AudioSource.h"
float TexturesStreamingHandler::CalculateTargetQuality(StreamableResource* resource, DateTime now)
float TexturesStreamingHandler::CalculateTargetQuality(StreamableResource* resource, DateTime now, double currentTime)
{
ASSERT(resource);
auto& texture = *(StreamingTexture*)resource;
const TextureHeader& header = *texture.GetHeader();
if (header.TextureGroup < 0 || header.TextureGroup >= Streaming::TextureGroups.Count())
float result = 1.0f;
if (header.TextureGroup >= 0 && header.TextureGroup < Streaming::TextureGroups.Count())
{
// Full texture load by default
return 1.0f;
}
// Quality based on texture group settings
const TextureGroup& group = Streaming::TextureGroups[header.TextureGroup];
result = group.Quality;
// Quality based on texture group settings
const TextureGroup& group = Streaming::TextureGroups[header.TextureGroup];
return group.QualityScale;
// Drop quality if invisible
const double lastRenderTime = texture.GetTexture()->LastRenderTime;
if (lastRenderTime < 0 || group.TimeToInvisible <= (float)(currentTime - lastRenderTime))
{
result *= group.QualityIfInvisible;
}
}
return result;
}
int32 TexturesStreamingHandler::CalculateResidency(StreamableResource* resource, float quality)
@@ -86,7 +93,7 @@ int32 TexturesStreamingHandler::CalculateRequestedResidency(StreamableResource*
return residency;
}
float ModelsStreamingHandler::CalculateTargetQuality(StreamableResource* resource, DateTime now)
float ModelsStreamingHandler::CalculateTargetQuality(StreamableResource* resource, DateTime now, double currentTime)
{
// TODO: calculate a proper quality levels for models based on render time and streaming enable/disable options
return 1.0f;
@@ -127,7 +134,7 @@ int32 ModelsStreamingHandler::CalculateRequestedResidency(StreamableResource* re
return residency;
}
float SkinnedModelsStreamingHandler::CalculateTargetQuality(StreamableResource* resource, DateTime now)
float SkinnedModelsStreamingHandler::CalculateTargetQuality(StreamableResource* resource, DateTime now, double currentTime)
{
// TODO: calculate a proper quality levels for models based on render time and streaming enable/disable options
return 1.0f;
@@ -168,7 +175,7 @@ int32 SkinnedModelsStreamingHandler::CalculateRequestedResidency(StreamableResou
return residency;
}
float AudioStreamingHandler::CalculateTargetQuality(StreamableResource* resource, DateTime now)
float AudioStreamingHandler::CalculateTargetQuality(StreamableResource* resource, DateTime now, double currentTime)
{
// Audio clips don't use quality but only residency
return 1.0f;

View File

@@ -11,7 +11,7 @@ class FLAXENGINE_API TexturesStreamingHandler : public IStreamingHandler
{
public:
// [IStreamingHandler]
float CalculateTargetQuality(StreamableResource* resource, DateTime now) override;
float CalculateTargetQuality(StreamableResource* resource, DateTime now, double currentTime) override;
int32 CalculateResidency(StreamableResource* resource, float quality) override;
int32 CalculateRequestedResidency(StreamableResource* resource, int32 targetResidency) override;
};
@@ -23,7 +23,7 @@ class FLAXENGINE_API ModelsStreamingHandler : public IStreamingHandler
{
public:
// [IStreamingHandler]
float CalculateTargetQuality(StreamableResource* resource, DateTime now) override;
float CalculateTargetQuality(StreamableResource* resource, DateTime now, double currentTime) override;
int32 CalculateResidency(StreamableResource* resource, float quality) override;
int32 CalculateRequestedResidency(StreamableResource* resource, int32 targetResidency) override;
};
@@ -35,7 +35,7 @@ class FLAXENGINE_API SkinnedModelsStreamingHandler : public IStreamingHandler
{
public:
// [IStreamingHandler]
float CalculateTargetQuality(StreamableResource* resource, DateTime now) override;
float CalculateTargetQuality(StreamableResource* resource, DateTime now, double currentTime) override;
int32 CalculateResidency(StreamableResource* resource, float quality) override;
int32 CalculateRequestedResidency(StreamableResource* resource, int32 targetResidency) override;
};
@@ -47,7 +47,7 @@ class FLAXENGINE_API AudioStreamingHandler : public IStreamingHandler
{
public:
// [IStreamingHandler]
float CalculateTargetQuality(StreamableResource* resource, DateTime now) override;
float CalculateTargetQuality(StreamableResource* resource, DateTime now, double currentTime) override;
int32 CalculateResidency(StreamableResource* resource, float quality) override;
int32 CalculateRequestedResidency(StreamableResource* resource, int32 targetResidency) override;
bool RequiresStreaming(StreamableResource* resource, int32 currentResidency, int32 targetResidency) override;

View File

@@ -26,7 +26,19 @@ DECLARE_SCRIPTING_TYPE_MINIMAL(TextureGroup);
/// The quality scale factor applied to textures in this group. Can be used to increase or decrease textures resolution. In range 0-1 where 0 means lowest quality, 1 means full quality.
/// </summary>
API_FIELD(Attributes="EditorOrder(20), Limit(0, 1)")
float QualityScale = 1.0f;
float Quality = 1.0f;
/// <summary>
/// The quality scale factor applied when texture is invisible for some time (defined by TimeToInvisible). Used to decrease texture quality when it's not rendered.
/// </summary>
API_FIELD(Attributes="EditorOrder(25), Limit(0, 1)")
float QualityIfInvisible = 0.5f;
/// <summary>
/// The time (in seconds) after which texture is considered to be invisible (if it's not rendered by a certain amount of time).
/// </summary>
API_FIELD(Attributes="EditorOrder(26), Limit(0)")
float TimeToInvisible = 20.0f;
/// <summary>
/// The minimum amount of loaded mip levels for textures in this group. Defines the amount of the mips that should be always loaded. Higher values decrease streaming usage and keep more mips loaded.