Merge pull request #4426 from aws-lumberyard-dev/Atom/santorac/FixSceneSrgTime

Fixed potential render scene time precision issues.

The timestamp was simply converted from GetTimeAtCurrentTick to a float. Since this value is backed by QueryPerformanceCounter which is 0 at boot, you could see broken animations on the GPU when your system has been on for a long time. So I simplified the RPI's time API (removed unused code), and subtracted the application start time each frame before converting the time value to a float.

Also moved FindShaderInputConstantIndex("m_time") to be called only once, instead of every frame.

Testing:
Originally: I had a local material shader that did vertex animation and it wasn't working at all before, and now it works.
More recently, I made local changes to StandardPBR to add a simple sin wave animation. I also modified GetTimeNowMicroSecond() to artificially add 30 days to the clock. This showed choppy animation before my changes, and smooth animation after.
AtomSampleViewer passed dx12 and vulkan (other than pre-existing issues)

Signed-off-by: santorac <55155825+santorac@users.noreply.github.com>
monroegm-disable-blank-issue-2
santorac 4 years ago
parent dcc2890da3
commit c5cb90ddb7

@ -97,8 +97,7 @@ namespace AZ
// SystemTickBus::OnTick // SystemTickBus::OnTick
void OnSystemTick() override; void OnSystemTick() override;
// Fill system time and game time information for simulation or rendering float GetCurrentTime();
void FillTickTimeInfo();
// The set of core asset handlers registered by the system. // The set of core asset handlers registered by the system.
AZStd::vector<AZStd::unique_ptr<Data::AssetHandler>> m_assetHandlers; AZStd::vector<AZStd::unique_ptr<Data::AssetHandler>> m_assetHandlers;
@ -124,7 +123,8 @@ namespace AZ
// The job policy used for feature processor's rendering prepare // The job policy used for feature processor's rendering prepare
RHI::JobPolicy m_prepareRenderJobPolicy = RHI::JobPolicy::Parallel; RHI::JobPolicy m_prepareRenderJobPolicy = RHI::JobPolicy::Parallel;
TickTimeInfo m_tickTime; ScriptTimePoint m_startTime;
float m_currentSimulationTime = 0.0f;
RPISystemDescriptor m_descriptor; RPISystemDescriptor m_descriptor;

@ -32,7 +32,6 @@ namespace AZ
namespace RPI namespace RPI
{ {
class Scene; class Scene;
struct TickTimeInfo;
class ShaderResourceGroup; class ShaderResourceGroup;
class AnyAsset; class AnyAsset;
class WindowContext; class WindowContext;
@ -203,7 +202,7 @@ namespace AZ
void OnRemovedFromScene(Scene* scene); void OnRemovedFromScene(Scene* scene);
// Called when this pipeline is about to be rendered // Called when this pipeline is about to be rendered
void OnStartFrame(const TickTimeInfo& tick); void OnStartFrame(float time);
// Called when the rendering of current frame is finished. // Called when the rendering of current frame is finished.
void OnFrameEnd(); void OnFrameEnd();

@ -48,14 +48,6 @@ namespace AZ
// Callback function to modify values of a ShaderResourceGroup // Callback function to modify values of a ShaderResourceGroup
using ShaderResourceGroupCallback = AZStd::function<void(ShaderResourceGroup*)>; using ShaderResourceGroupCallback = AZStd::function<void(ShaderResourceGroup*)>;
//! A structure for ticks which contains system time and game time.
struct TickTimeInfo
{
float m_currentGameTime;
float m_gameDeltaTime = 0;
};
class Scene final class Scene final
: public SceneRequestBus::Handler : public SceneRequestBus::Handler
{ {
@ -179,12 +171,14 @@ namespace AZ
// Cpu simulation which runs all active FeatureProcessor Simulate() functions. // Cpu simulation which runs all active FeatureProcessor Simulate() functions.
// @param jobPolicy if it's JobPolicy::Parallel, the function will spawn a job thread for each FeatureProcessor's simulation. // @param jobPolicy if it's JobPolicy::Parallel, the function will spawn a job thread for each FeatureProcessor's simulation.
void Simulate(const TickTimeInfo& tickInfo, RHI::JobPolicy jobPolicy); // @param simulationTime the number of seconds since the application started
void Simulate(RHI::JobPolicy jobPolicy, float simulationTime);
// Collect DrawPackets from FeatureProcessors // Collect DrawPackets from FeatureProcessors
// @param jobPolicy if it's JobPolicy::Parallel, the function will spawn a job thread for each FeatureProcessor's // @param jobPolicy if it's JobPolicy::Parallel, the function will spawn a job thread for each FeatureProcessor's
// PrepareRender. // PrepareRender.
void PrepareRender(const TickTimeInfo& tickInfo, RHI::JobPolicy jobPolicy); // @param simulationTime the number of seconds since the application started; this is the same time value that was passed to Simulate()
void PrepareRender(RHI::JobPolicy jobPolicy, float simulationTime);
// Function called when the current frame is finished rendering. // Function called when the current frame is finished rendering.
void OnFrameEnd(); void OnFrameEnd();
@ -267,6 +261,7 @@ namespace AZ
// Registry which allocates draw filter tag for RenderPipeline // Registry which allocates draw filter tag for RenderPipeline
RHI::Ptr<RHI::DrawFilterTagRegistry> m_drawFilterTagRegistry; RHI::Ptr<RHI::DrawFilterTagRegistry> m_drawFilterTagRegistry;
RHI::ShaderInputConstantIndex m_timeInputIndex;
float m_simulationTime; float m_simulationTime;
}; };

@ -268,21 +268,23 @@ namespace AZ
AssetInitBus::Broadcast(&AssetInitBus::Events::PostLoadInit); AssetInitBus::Broadcast(&AssetInitBus::Events::PostLoadInit);
// Update tick time info m_currentSimulationTime = GetCurrentTime();
FillTickTimeInfo();
for (auto& scene : m_scenes) for (auto& scene : m_scenes)
{ {
scene->Simulate(m_tickTime, m_simulationJobPolicy); scene->Simulate(m_simulationJobPolicy, m_currentSimulationTime);
} }
} }
void RPISystem::FillTickTimeInfo() float RPISystem::GetCurrentTime()
{ {
AZ::TickRequestBus::BroadcastResult(m_tickTime.m_gameDeltaTime, &AZ::TickRequestBus::Events::GetTickDeltaTime); ScriptTimePoint timeAtCurrentTick;
ScriptTimePoint currentTime; AZ::TickRequestBus::BroadcastResult(timeAtCurrentTick, &AZ::TickRequestBus::Events::GetTimeAtCurrentTick);
AZ::TickRequestBus::BroadcastResult(currentTime, &AZ::TickRequestBus::Events::GetTimeAtCurrentTick);
m_tickTime.m_currentGameTime = static_cast<float>(currentTime.GetSeconds()); // We subtract the start time to maximize precision of the time value, since we will be converting it to a float.
double currentTime = timeAtCurrentTick.GetSeconds() - m_startTime.GetSeconds();
return aznumeric_cast<float>(currentTime);
} }
void RPISystem::RenderTick() void RPISystem::RenderTick()
@ -301,7 +303,7 @@ namespace AZ
// [GFX TODO] We may parallel scenes' prepare render. // [GFX TODO] We may parallel scenes' prepare render.
for (auto& scenePtr : m_scenes) for (auto& scenePtr : m_scenes)
{ {
scenePtr->PrepareRender(m_tickTime, m_prepareRenderJobPolicy); scenePtr->PrepareRender(m_prepareRenderJobPolicy, m_currentSimulationTime);
} }
m_rhiSystem.FrameUpdate( m_rhiSystem.FrameUpdate(

@ -375,7 +375,7 @@ namespace AZ
m_scene->RemoveRenderPipeline(m_nameId); m_scene->RemoveRenderPipeline(m_nameId);
} }
void RenderPipeline::OnStartFrame([[maybe_unused]] const TickTimeInfo& tick) void RenderPipeline::OnStartFrame([[maybe_unused]] float time)
{ {
AZ_PROFILE_SCOPE(RPI, "RenderPipeline: OnStartFrame"); AZ_PROFILE_SCOPE(RPI, "RenderPipeline: OnStartFrame");

@ -44,6 +44,9 @@ namespace AZ
{ {
auto shaderAsset = RPISystemInterface::Get()->GetCommonShaderAssetForSrgs(); auto shaderAsset = RPISystemInterface::Get()->GetCommonShaderAssetForSrgs();
scene->m_srg = ShaderResourceGroup::Create(shaderAsset, sceneSrgLayout->GetName()); scene->m_srg = ShaderResourceGroup::Create(shaderAsset, sceneSrgLayout->GetName());
// Set value for constants defined in SceneTimeSrg.azsli
scene->m_timeInputIndex = scene->m_srg->FindShaderInputConstantIndex(Name{ "m_time" });
} }
scene->m_name = sceneDescriptor.m_nameId; scene->m_name = sceneDescriptor.m_nameId;
@ -410,11 +413,11 @@ namespace AZ
//[GFX TODO]: the completion job should start here //[GFX TODO]: the completion job should start here
} }
void Scene::Simulate([[maybe_unused]] const TickTimeInfo& tickInfo, RHI::JobPolicy jobPolicy) void Scene::Simulate(RHI::JobPolicy jobPolicy, float simulationTime)
{ {
AZ_PROFILE_SCOPE(RPI, "Scene: Simulate"); AZ_PROFILE_SCOPE(RPI, "Scene: Simulate");
m_simulationTime = tickInfo.m_currentGameTime; m_simulationTime = simulationTime;
// If previous simulation job wasn't done, wait for it to finish. // If previous simulation job wasn't done, wait for it to finish.
if (m_taskGraphActive) if (m_taskGraphActive)
@ -483,11 +486,9 @@ namespace AZ
{ {
if (m_srg) if (m_srg)
{ {
// Set value for constants defined in SceneTimeSrg.azsli if (m_timeInputIndex.IsValid())
RHI::ShaderInputConstantIndex timeIndex = m_srg->FindShaderInputConstantIndex(Name{ "m_time" });
if (timeIndex.IsValid())
{ {
m_srg->SetConstant(timeIndex, m_simulationTime); m_srg->SetConstant(m_timeInputIndex, m_simulationTime);
} }
// signal any handlers to update values for their partial scene srg // signal any handlers to update values for their partial scene srg
@ -620,7 +621,7 @@ namespace AZ
WaitAndCleanCompletionJob(finalizeDrawListsCompletion); WaitAndCleanCompletionJob(finalizeDrawListsCompletion);
} }
void Scene::PrepareRender(const TickTimeInfo& tickInfo, RHI::JobPolicy jobPolicy) void Scene::PrepareRender(RHI::JobPolicy jobPolicy, float simulationTime)
{ {
AZ_PROFILE_SCOPE(RPI, "Scene: PrepareRender"); AZ_PROFILE_SCOPE(RPI, "Scene: PrepareRender");
@ -644,7 +645,7 @@ namespace AZ
if (pipeline->NeedsRender()) if (pipeline->NeedsRender())
{ {
activePipelines.push_back(pipeline); activePipelines.push_back(pipeline);
pipeline->OnStartFrame(tickInfo); pipeline->OnStartFrame(simulationTime);
} }
} }
} }

Loading…
Cancel
Save