Merge pull request #4251 from aws-lumberyard-dev/nvsickle/RevertFrameTimer

Revert "Refresh rate driven rendering tick logic (#3375)"
monroegm-disable-blank-issue-2
SergeyAMZN 4 years ago committed by GitHub
commit 08e020f2b1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -44,7 +44,7 @@ enum
{ {
// in milliseconds // in milliseconds
GameModeIdleFrequency = 0, GameModeIdleFrequency = 0,
EditorModeIdleFrequency = 0, EditorModeIdleFrequency = 1,
InactiveModeFrequency = 10, InactiveModeFrequency = 10,
UninitializedFrequency = 9999, UninitializedFrequency = 9999,
}; };

@ -36,9 +36,6 @@
#include "CryEdit.h" #include "CryEdit.h"
#include "Viewport.h" #include "Viewport.h"
// Atom Renderer
#include <Atom/RPI.Public/RPISystemInterface.h>
AZ_PUSH_DISABLE_DLL_EXPORT_MEMBER_WARNING AZ_PUSH_DISABLE_DLL_EXPORT_MEMBER_WARNING
#include <TrackView/ui_SequenceBatchRenderDialog.h> #include <TrackView/ui_SequenceBatchRenderDialog.h>
AZ_POP_DISABLE_DLL_EXPORT_MEMBER_WARNING AZ_POP_DISABLE_DLL_EXPORT_MEMBER_WARNING
@ -1237,13 +1234,6 @@ void CSequenceBatchRenderDialog::OnKickIdleTimout()
{ {
componentApplication->TickSystem(); componentApplication->TickSystem();
} }
// Directly tick the renderer, as it's no longer part of the system tick
if (auto rpiSystem = AZ::RPI::RPISystemInterface::Get())
{
rpiSystem->SimulationTick();
rpiSystem->RenderTick();
}
} }
} }

@ -74,8 +74,6 @@
#include <AzCore/Module/Environment.h> #include <AzCore/Module/Environment.h>
#include <AzCore/std/string/conversions.h> #include <AzCore/std/string/conversions.h>
AZ_CVAR(float, g_simulation_tick_rate, 0, nullptr, AZ::ConsoleFunctorFlags::Null, "The rate at which the game simulation tick loop runs, or 0 for as fast as possible");
static void PrintEntityName(const AZ::ConsoleCommandContainer& arguments) static void PrintEntityName(const AZ::ConsoleCommandContainer& arguments)
{ {
if (arguments.empty()) if (arguments.empty())
@ -1396,23 +1394,6 @@ namespace AZ
AZ_PROFILE_SCOPE(AzCore, "ComponentApplication::Tick:OnTick"); AZ_PROFILE_SCOPE(AzCore, "ComponentApplication::Tick:OnTick");
EBUS_EVENT(TickBus, OnTick, m_deltaTime, ScriptTimePoint(now)); EBUS_EVENT(TickBus, OnTick, m_deltaTime, ScriptTimePoint(now));
} }
// If tick rate limiting is on, ensure (1 / g_simulation_tick_rate) ms has elapsed since the last frame,
// sleeping if there's still time remaining.
if (g_simulation_tick_rate > 0.f)
{
now = AZStd::chrono::system_clock::now();
// Work in microsecond durations here as that's the native measurement time for time_point
constexpr float microsecondsPerSecond = 1000.f * 1000.f;
const AZStd::chrono::microseconds timeBudgetPerTick(static_cast<int>(microsecondsPerSecond / g_simulation_tick_rate));
AZStd::chrono::microseconds timeUntilNextTick = m_currentTime + timeBudgetPerTick - now;
if (timeUntilNextTick.count() > 0)
{
AZStd::this_thread::sleep_for(timeUntilNextTick);
}
}
} }
} }

@ -46,8 +46,6 @@ namespace AZ
TICK_PRE_RENDER = 750, ///< Suggested tick handler position to update render-related data. TICK_PRE_RENDER = 750, ///< Suggested tick handler position to update render-related data.
TICK_RENDER = 800, ///< Suggested tick handler position for rendering.
TICK_DEFAULT = 1000, ///< Default tick handler position when the handler is constructed. TICK_DEFAULT = 1000, ///< Default tick handler position when the handler is constructed.
TICK_UI = 2000, ///< Suggested tick handler position for UI components. TICK_UI = 2000, ///< Suggested tick handler position for UI components.

@ -56,8 +56,7 @@ namespace AZ
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
virtual void OnBootstrapSceneReady([[maybe_unused]]AZ::RPI::Scene* bootstrapScene){} virtual void OnBootstrapSceneReady(AZ::RPI::Scene* bootstrapScene) = 0;
virtual void OnFrameRateLimitChanged([[maybe_unused]]float fpsLimit){}
}; };
using NotificationBus = AZ::EBus<Notification>; using NotificationBus = AZ::EBus<Notification>;
} // namespace Bootstrap } // namespace Bootstrap

@ -23,8 +23,6 @@ namespace AZ::Render::Bootstrap
virtual AZ::RPI::ScenePtr GetOrCreateAtomSceneFromAzScene(AzFramework::Scene* scene) = 0; virtual AZ::RPI::ScenePtr GetOrCreateAtomSceneFromAzScene(AzFramework::Scene* scene) = 0;
virtual bool EnsureDefaultRenderPipelineInstalledForScene(AZ::RPI::ScenePtr scene, AZ::RPI::ViewportContextPtr viewportContext) = 0; virtual bool EnsureDefaultRenderPipelineInstalledForScene(AZ::RPI::ScenePtr scene, AZ::RPI::ViewportContextPtr viewportContext) = 0;
virtual float GetFrameRateLimit() const = 0;
virtual void SetFrameRateLimit(float fpsLimit) = 0;
protected: protected:
~Request() = default; ~Request() = default;

@ -42,14 +42,7 @@
#include <AzCore/Console/IConsole.h> #include <AzCore/Console/IConsole.h>
#include <BootstrapSystemComponent_Traits_Platform.h> #include <BootstrapSystemComponent_Traits_Platform.h>
static void OnFrameRateLimitChanged(const float& fpsLimit)
{
AZ::Render::Bootstrap::RequestBus::Broadcast(
&AZ::Render::Bootstrap::RequestBus::Events::SetFrameRateLimit, fpsLimit);
}
AZ_CVAR(AZ::CVarFixedString, r_default_pipeline_name, AZ_TRAIT_BOOTSTRAPSYSTEMCOMPONENT_PIPELINE_NAME, nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "Default Render pipeline name"); AZ_CVAR(AZ::CVarFixedString, r_default_pipeline_name, AZ_TRAIT_BOOTSTRAPSYSTEMCOMPONENT_PIPELINE_NAME, nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "Default Render pipeline name");
AZ_CVAR(float, r_fps_limit, 0, OnFrameRateLimitChanged, AZ::ConsoleFunctorFlags::Null, "The maximum framerate to render at, or 0 for unlimited");
namespace AZ namespace AZ
{ {
@ -358,22 +351,6 @@ namespace AZ
return true; return true;
} }
float BootstrapSystemComponent::GetFrameRateLimit() const
{
return r_fps_limit;
}
void BootstrapSystemComponent::SetFrameRateLimit(float fpsLimit)
{
r_fps_limit = fpsLimit;
if (m_viewportContext)
{
m_viewportContext->SetFpsLimit(r_fps_limit);
}
Render::Bootstrap::NotificationBus::Broadcast(
&Render::Bootstrap::NotificationBus::Events::OnFrameRateLimitChanged, fpsLimit);
}
void BootstrapSystemComponent::CreateDefaultRenderPipeline() void BootstrapSystemComponent::CreateDefaultRenderPipeline()
{ {
EnsureDefaultRenderPipelineInstalledForScene(m_defaultScene, m_viewportContext); EnsureDefaultRenderPipelineInstalledForScene(m_defaultScene, m_viewportContext);
@ -413,11 +390,23 @@ namespace AZ
} }
void BootstrapSystemComponent::OnTick([[maybe_unused]] float deltaTime, [[maybe_unused]] ScriptTimePoint time) void BootstrapSystemComponent::OnTick([[maybe_unused]] float deltaTime, [[maybe_unused]] ScriptTimePoint time)
{ } {
// Temp: When running in the launcher without the legacy renderer
// we need to call RenderTick on the viewport context each frame.
if (m_viewportContext)
{
AZ::ApplicationTypeQuery appType;
ComponentApplicationBus::Broadcast(&AZ::ComponentApplicationBus::Events::QueryApplicationType, appType);
if (appType.IsGame())
{
m_viewportContext->RenderTick();
}
}
}
int BootstrapSystemComponent::GetTickOrder() int BootstrapSystemComponent::GetTickOrder()
{ {
return TICK_PRE_RENDER; return TICK_LAST;
} }
void BootstrapSystemComponent::OnWindowClosed() void BootstrapSystemComponent::OnWindowClosed()

@ -69,8 +69,6 @@ namespace AZ
// Render::Bootstrap::RequestBus::Handler overrides ... // Render::Bootstrap::RequestBus::Handler overrides ...
AZ::RPI::ScenePtr GetOrCreateAtomSceneFromAzScene(AzFramework::Scene* scene) override; AZ::RPI::ScenePtr GetOrCreateAtomSceneFromAzScene(AzFramework::Scene* scene) override;
bool EnsureDefaultRenderPipelineInstalledForScene(AZ::RPI::ScenePtr scene, AZ::RPI::ViewportContextPtr viewportContext) override; bool EnsureDefaultRenderPipelineInstalledForScene(AZ::RPI::ScenePtr scene, AZ::RPI::ViewportContextPtr viewportContext) override;
float GetFrameRateLimit() const override;
void SetFrameRateLimit(float fpsLimit) override;
protected: protected:
// Component overrides ... // Component overrides ...

@ -161,14 +161,9 @@ namespace AZ
//! Add this RenderPipeline to RPI system's RenderTick and it will be rendered whenever //! Add this RenderPipeline to RPI system's RenderTick and it will be rendered whenever
//! the RPI system's RenderTick is called. //! the RPI system's RenderTick is called.
//! The RenderPipeline is rendered per RenderTick by default. //! The RenderPipeline is rendered per RenderTick by default unless AddToRenderTickOnce() was called.
void AddToRenderTick(); void AddToRenderTick();
//! Add this RenderPipeline to RPI system's RenderTick and it will be rendered every RenderTick
//! after the specified interval has elapsed since the last rendered frame.
//! @param renderInterval The desired time between rendered frames, in seconds.
void AddToRenderTickAtInterval(AZStd::chrono::duration<float> renderInterval);
//! Disable render for this RenderPipeline //! Disable render for this RenderPipeline
void RemoveFromRenderTick(); void RemoveFromRenderTick();
@ -176,10 +171,9 @@ namespace AZ
enum class RenderMode : uint8_t enum class RenderMode : uint8_t
{ {
RenderEveryTick, //!< Render at each RPI system render tick. RenderEveryTick, // Render at each RPI system render tick
RenderAtTargetRate, //!< Render on RPI system render tick after a target refresh rate interval has passed. RenderOnce, // Render once in next RPI system render tick
RenderOnce, //!< Render once in next RPI system render tick. NoRender // Render disabled.
NoRender //!< Render disabled.
}; };
//! Get current render mode //! Get current render mode
@ -191,12 +185,6 @@ namespace AZ
//! Get draw filter mask //! Get draw filter mask
RHI::DrawFilterMask GetDrawFilterMask() const; RHI::DrawFilterMask GetDrawFilterMask() const;
using FrameNotificationEvent = AZ::Event<>;
//! Notifies a listener when a frame is about to be prepared for render, before SRGs are bound.
void ConnectPrepareFrameHandler(FrameNotificationEvent::Handler& handler);
//! Notifies a listener when the rendering of a frame has finished
void ConnectEndFrameHandler(FrameNotificationEvent::Handler& handler);
private: private:
RenderPipeline() = default; RenderPipeline() = default;
@ -214,11 +202,8 @@ namespace AZ
void OnAddedToScene(Scene* scene); void OnAddedToScene(Scene* scene);
void OnRemovedFromScene(Scene* scene); void OnRemovedFromScene(Scene* scene);
// Called before this pipeline is about to be rendered and before SRGs are bound.
void OnPrepareFrame();
// Called when this pipeline is about to be rendered // Called when this pipeline is about to be rendered
void OnStartFrame(); void OnStartFrame(const TickTimeInfo& tick);
// Called when the rendering of current frame is finished. // Called when the rendering of current frame is finished.
void OnFrameEnd(); void OnFrameEnd();
@ -243,14 +228,8 @@ namespace AZ
PipelineViewMap m_pipelineViewsByTag; PipelineViewMap m_pipelineViewsByTag;
// The system time when the last time this pipeline render was started /// The system time when the last time this pipeline render was started
AZStd::chrono::system_clock::time_point m_lastRenderStartTime; float m_lastRenderStartTime = 0;
// The current system time, as of OnPrepareFrame's execution.
AZStd::chrono::system_clock::time_point m_lastRenderRequestTime;
// The target time between renders when m_renderMode is RenderMode::RenderAtTargetRate
AZStd::chrono::duration<float> m_targetRefreshRate;
// RenderPipeline's name id, it will be used to identify the render pipeline when it's added to a Scene // RenderPipeline's name id, it will be used to identify the render pipeline when it's added to a Scene
RenderPipelineId m_nameId; RenderPipelineId m_nameId;
@ -281,10 +260,6 @@ namespace AZ
// A mask to filter draw items submitted by passes of this render pipeline. // A mask to filter draw items submitted by passes of this render pipeline.
// This mask is created from the value of m_drawFilterTag. // This mask is created from the value of m_drawFilterTag.
RHI::DrawFilterMask m_drawFilterMask = 0; RHI::DrawFilterMask m_drawFilterMask = 0;
// Events for notification on render state
FrameNotificationEvent m_prepareFrameEvent;
FrameNotificationEvent m_endFrameEvent;
}; };
} // namespace RPI } // namespace RPI

@ -67,9 +67,6 @@ namespace AZ
//! Notifies when the PrepareRender phase is ending //! Notifies when the PrepareRender phase is ending
virtual void OnEndPrepareRender() {} virtual void OnEndPrepareRender() {}
//! Notifies when the render tick for a given frame has finished.
virtual void OnFrameEnd() {}
}; };
using SceneNotificationBus = AZ::EBus<SceneNotification>; using SceneNotificationBus = AZ::EBus<SceneNotification>;

@ -51,21 +51,9 @@ namespace AZ
//! Sets the root scene associated with this viewport. //! Sets the root scene associated with this viewport.
//! This does not provide a default render pipeline, one must be provided to enable rendering. //! This does not provide a default render pipeline, one must be provided to enable rendering.
void SetRenderScene(ScenePtr scene); void SetRenderScene(ScenePtr scene);
//! Runs one simulation and render tick and renders a frame to this viewport's window.
//! Gets the maximum frame rate this viewport context's pipeline can render at, 0 for unlimited. //! @note This is likely to be replaced by a tick management system in the RPI.
//! The target framerate for the pipeline will be determined by this frame limit and the void RenderTick();
//! vsync settings for the current window.
float GetFpsLimit() const;
//! Sets the maximum frame rate this viewport context's pipeline can render at, 0 for unlimited.
//! The target framerate for the pipeline will be determined by this frame limit and the
//! vsync settings for the current window.
void SetFpsLimit(float fpsLimit);
//! Gets the target frame rate for this viewport context.
//! This returns the lowest of either the current VSync refresh rate
//! or 0 for an unlimited frame rate (if there's no FPS limit and vsync is off).
float GetTargetFrameRate() const;
//! Gets the current name of this ViewportContext. //! Gets the current name of this ViewportContext.
//! This name is used to tie this ViewportContext to its View stack, and ViewportContexts may be //! This name is used to tie this ViewportContext to its View stack, and ViewportContexts may be
@ -86,28 +74,19 @@ namespace AZ
//! \see AzFramework::WindowRequests::GetDpiScaleFactor //! \see AzFramework::WindowRequests::GetDpiScaleFactor
float GetDpiScalingFactor() const; float GetDpiScalingFactor() const;
//! Gets the current vsync interval, as a divisor of the current refresh rate.
//! A value of 0 indicates that vsync is disabled.
uint32_t GetVsyncInterval() const;
//! Gets the current display refresh rate, in frames per second.
uint32_t GetRefreshRate() const;
// SceneNotificationBus interface overrides... // SceneNotificationBus interface overrides...
//! Ensures our default view remains set when our scene's render pipelines are modified. //! Ensures our default view remains set when our scene's render pipelines are modified.
void OnRenderPipelineAdded(RenderPipelinePtr pipeline) override; void OnRenderPipelineAdded(RenderPipelinePtr pipeline) override;
//! Ensures our default view remains set when our scene's render pipelines are modified. //! Ensures our default view remains set when our scene's render pipelines are modified.
void OnRenderPipelineRemoved(RenderPipeline* pipeline) override; void OnRenderPipelineRemoved(RenderPipeline* pipeline) override;
//! OnBeginPrepareRender is forwarded to our RenderTick notification to allow subscribers to do rendering.
void OnBeginPrepareRender() override;
// WindowNotificationBus interface overrides... // WindowNotificationBus interface overrides...
//! Used to fire a notification when our window resizes. //! Used to fire a notification when our window resizes.
void OnWindowResized(uint32_t width, uint32_t height) override; void OnWindowResized(uint32_t width, uint32_t height) override;
//! Used to fire a notification when our window DPI changes. //! Used to fire a notification when our window DPI changes.
void OnDpiScaleFactorChanged(float dpiScaleFactor) override; void OnDpiScaleFactorChanged(float dpiScaleFactor) override;
//! Used to fire a notification when our vsync interval changes.
void OnVsyncIntervalChanged(uint32_t interval) override;
//! Used to fire a notification when our refresh rate changes.
void OnRefreshRateChanged(uint32_t refreshRate) override;
using SizeChangedEvent = AZ::Event<AzFramework::WindowSize>; using SizeChangedEvent = AZ::Event<AzFramework::WindowSize>;
//! Notifies consumers when the viewport size has changed. //! Notifies consumers when the viewport size has changed.
@ -119,12 +98,6 @@ namespace AZ
//! Alternatively, connect to ViewportContextNotificationsBus and listen to ViewportContextNotifications::OnViewportDpiScalingChanged. //! Alternatively, connect to ViewportContextNotificationsBus and listen to ViewportContextNotifications::OnViewportDpiScalingChanged.
void ConnectDpiScalingFactorChangedHandler(ScalarChangedEvent::Handler& handler); void ConnectDpiScalingFactorChangedHandler(ScalarChangedEvent::Handler& handler);
using UintChangedEvent = AZ::Event<uint32_t>;
//! Notifies consumers when the vsync interval has changed.
void ConnectVsyncIntervalChangedHandler(UintChangedEvent::Handler& handler);
//! Notifies consumers when the refresh rate has changed.
void ConnectRefreshRateChangedHandler(UintChangedEvent::Handler& handler);
using MatrixChangedEvent = AZ::Event<const AZ::Matrix4x4&>; using MatrixChangedEvent = AZ::Event<const AZ::Matrix4x4&>;
//! Notifies consumers when the view matrix has changed. //! Notifies consumers when the view matrix has changed.
void ConnectViewMatrixChangedHandler(MatrixChangedEvent::Handler& handler); void ConnectViewMatrixChangedHandler(MatrixChangedEvent::Handler& handler);
@ -166,24 +139,15 @@ namespace AZ
void SetDefaultView(ViewPtr view); void SetDefaultView(ViewPtr view);
// Ensures our render pipeline's default camera matches ours. // Ensures our render pipeline's default camera matches ours.
void UpdatePipelineView(); void UpdatePipelineView();
// Ensures our render pipeline refresh rate matches our refresh rate.
void UpdatePipelineRefreshRate();
// Resets the current pipeline reference and ensures pipeline events are disconnected.
void ResetCurrentPipeline();
ScenePtr m_rootScene; ScenePtr m_rootScene;
WindowContextSharedPtr m_windowContext; WindowContextSharedPtr m_windowContext;
ViewPtr m_defaultView; ViewPtr m_defaultView;
AzFramework::WindowSize m_viewportSize; AzFramework::WindowSize m_viewportSize;
float m_viewportDpiScaleFactor = 1.0f; float m_viewportDpiScaleFactor = 1.0f;
uint32_t m_vsyncInterval = 1;
uint32_t m_refreshRate = 60;
float m_fpsLimit = 0.f;
SizeChangedEvent m_sizeChangedEvent; SizeChangedEvent m_sizeChangedEvent;
ScalarChangedEvent m_dpiScalingFactorChangedEvent; ScalarChangedEvent m_dpiScalingFactorChangedEvent;
UintChangedEvent m_vsyncIntervalChangedEvent;
UintChangedEvent m_refreshRateChangedEvent;
MatrixChangedEvent m_viewMatrixChangedEvent; MatrixChangedEvent m_viewMatrixChangedEvent;
MatrixChangedEvent::Handler m_onViewMatrixChangedHandler; MatrixChangedEvent::Handler m_onViewMatrixChangedHandler;
MatrixChangedEvent m_projectionMatrixChangedEvent; MatrixChangedEvent m_projectionMatrixChangedEvent;
@ -193,9 +157,6 @@ namespace AZ
ViewChangedEvent m_defaultViewChangedEvent; ViewChangedEvent m_defaultViewChangedEvent;
ViewportIdEvent m_aboutToBeDestroyedEvent; ViewportIdEvent m_aboutToBeDestroyedEvent;
AZ::Event<>::Handler m_prepareFrameHandler;
AZ::Event<>::Handler m_endFrameHandler;
ViewportContextManager* m_manager; ViewportContextManager* m_manager;
RenderPipelinePtr m_currentPipeline; RenderPipelinePtr m_currentPipeline;
Name m_name; Name m_name;

@ -110,13 +110,11 @@ namespace AZ
virtual void OnViewportSizeChanged(AzFramework::WindowSize size){AZ_UNUSED(size);} virtual void OnViewportSizeChanged(AzFramework::WindowSize size){AZ_UNUSED(size);}
//! Called when the window DPI scaling changes for a given viewport context. //! Called when the window DPI scaling changes for a given viewport context.
virtual void OnViewportDpiScalingChanged(float dpiScale){AZ_UNUSED(dpiScale);} virtual void OnViewportDpiScalingChanged(float dpiScale){AZ_UNUSED(dpiScale);}
//! Called when the active view changes for a given viewport context. //! Called when the active view for a given viewport context name changes.
virtual void OnViewportDefaultViewChanged(AZ::RPI::ViewPtr view){AZ_UNUSED(view);} virtual void OnViewportDefaultViewChanged(AZ::RPI::ViewPtr view){AZ_UNUSED(view);}
//! Called when the viewport is to be rendered. //! Called when the viewport is to be rendered.
//! Add draws to this function if they only need to be rendered to this viewport. //! Add draws to this functions if they only need to be rendered to this viewport.
virtual void OnRenderTick(){} virtual void OnRenderTick(){};
//! Called when the viewport finishes rendering a frame.
virtual void OnFrameEnd(){}
protected: protected:
~ViewportContextNotifications() = default; ~ViewportContextNotifications() = default;

@ -14,9 +14,6 @@
#include <RPI.Private/RPISystemComponent.h> #include <RPI.Private/RPISystemComponent.h>
#include <Atom/RHI/Factory.h> #include <Atom/RHI/Factory.h>
#include <Atom/RPI.Public/ViewportContextBus.h>
#include <Atom/RPI.Public/ViewportContext.h>
#include <Atom/RPI.Public/RenderPipeline.h>
#include <AzCore/Asset/AssetManager.h> #include <AzCore/Asset/AssetManager.h>
#include <AzCore/IO/IOUtils.h> #include <AzCore/IO/IOUtils.h>
@ -96,29 +93,20 @@ namespace AZ
} }
m_rpiSystem.Initialize(m_rpiDescriptor); m_rpiSystem.Initialize(m_rpiDescriptor);
AZ::TickBus::Handler::BusConnect(); AZ::SystemTickBus::Handler::BusConnect();
} }
void RPISystemComponent::Deactivate() void RPISystemComponent::Deactivate()
{ {
AZ::TickBus::Handler::BusDisconnect(); AZ::SystemTickBus::Handler::BusDisconnect();
m_rpiSystem.Shutdown(); m_rpiSystem.Shutdown();
} }
void RPISystemComponent::OnTick([[maybe_unused]]float deltaTime, [[maybe_unused]]ScriptTimePoint time) void RPISystemComponent::OnSystemTick()
{ {
if (deltaTime == 0.f)
{
return;
}
m_rpiSystem.SimulationTick(); m_rpiSystem.SimulationTick();
m_rpiSystem.RenderTick(); m_rpiSystem.RenderTick();
} }
int RPISystemComponent::GetTickOrder()
{
return AZ::ComponentTickBus::TICK_RENDER;
}
} // namespace RPI } // namespace RPI
} // namespace AZ } // namespace AZ

@ -32,7 +32,7 @@ namespace AZ
*/ */
class RPISystemComponent final class RPISystemComponent final
: public AZ::Component : public AZ::Component
, private AZ::TickBus::Handler , public AZ::SystemTickBus::Handler
{ {
public: public:
AZ_COMPONENT(RPISystemComponent, "{83E301F3-7A0C-4099-B530-9342B91B1BC0}"); AZ_COMPONENT(RPISystemComponent, "{83E301F3-7A0C-4099-B530-9342B91B1BC0}");
@ -50,9 +50,8 @@ namespace AZ
private: private:
RPISystemComponent(const RPISystemComponent&) = delete; RPISystemComponent(const RPISystemComponent&) = delete;
// TickBus overrides... // SystemTickBus overrides...
void OnTick(float deltaTime, ScriptTimePoint time) override; void OnSystemTick() override;
int GetTickOrder() override;
RPISystem m_rpiSystem; RPISystem m_rpiSystem;

@ -92,13 +92,10 @@ namespace AZ
// --- Enable/Disable --- // --- Enable/Disable ---
void Pass::SetEnabled(bool enabled) void Pass::SetEnabled(bool enabled)
{
if (m_flags.m_enabled != enabled)
{ {
m_flags.m_enabled = enabled; m_flags.m_enabled = enabled;
OnHierarchyChange(); OnHierarchyChange();
} }
}
bool Pass::IsEnabled() const bool Pass::IsEnabled() const
{ {

@ -301,26 +301,6 @@ namespace AZ
m_drawFilterMask = 0; m_drawFilterMask = 0;
} }
void RenderPipeline::OnPrepareFrame()
{
m_lastRenderRequestTime = AZStd::chrono::system_clock::now();
// If we're attempting to render at a target interval, check to see if we're within
// 1ms of that interval, enabling rendering only if we are.
if (m_renderMode == RenderMode::RenderAtTargetRate)
{
constexpr AZStd::chrono::duration<float> updateThresholdMs(0.001f);
const bool shouldRender =
m_lastRenderRequestTime - m_lastRenderStartTime + updateThresholdMs >= m_targetRefreshRate;
m_rootPass->SetEnabled(shouldRender);
}
if (NeedsRender())
{
m_prepareFrameEvent.Signal();
}
}
void RenderPipeline::OnPassModified() void RenderPipeline::OnPassModified()
{ {
if (m_needsPassRecreate) if (m_needsPassRecreate)
@ -395,11 +375,11 @@ namespace AZ
m_scene->RemoveRenderPipeline(m_nameId); m_scene->RemoveRenderPipeline(m_nameId);
} }
void RenderPipeline::OnStartFrame() void RenderPipeline::OnStartFrame(const TickTimeInfo& tick)
{ {
AZ_PROFILE_SCOPE(RPI, "RenderPipeline: OnStartFrame"); AZ_PROFILE_SCOPE(RPI, "RenderPipeline: OnStartFrame");
m_lastRenderStartTime = m_lastRenderRequestTime; m_lastRenderStartTime = tick.m_currentGameTime;
OnPassModified(); OnPassModified();
@ -427,7 +407,6 @@ namespace AZ
{ {
RemoveFromRenderTick(); RemoveFromRenderTick();
} }
m_endFrameEvent.Signal();
} }
void RenderPipeline::CollectPersistentViews(AZStd::map<ViewPtr, RHI::DrawListMask>& outViewMasks) const void RenderPipeline::CollectPersistentViews(AZStd::map<ViewPtr, RHI::DrawListMask>& outViewMasks) const
@ -510,13 +489,6 @@ namespace AZ
m_renderMode = RenderMode::RenderEveryTick; m_renderMode = RenderMode::RenderEveryTick;
} }
void RenderPipeline::AddToRenderTickAtInterval(AZStd::chrono::duration<float> renderInterval)
{
m_rootPass->SetEnabled(false);
m_renderMode = RenderMode::RenderAtTargetRate;
m_targetRefreshRate = renderInterval;
}
void RenderPipeline::RemoveFromRenderTick() void RenderPipeline::RemoveFromRenderTick()
{ {
m_renderMode = RenderMode::NoRender; m_renderMode = RenderMode::NoRender;
@ -530,7 +502,7 @@ namespace AZ
bool RenderPipeline::NeedsRender() const bool RenderPipeline::NeedsRender() const
{ {
return m_rootPass->IsEnabled(); return m_renderMode != RenderMode::NoRender;
} }
RHI::DrawFilterTag RenderPipeline::GetDrawFilterTag() const RHI::DrawFilterTag RenderPipeline::GetDrawFilterTag() const
@ -543,16 +515,6 @@ namespace AZ
return m_drawFilterMask; return m_drawFilterMask;
} }
void RenderPipeline::ConnectPrepareFrameHandler(FrameNotificationEvent::Handler& handler)
{
handler.Connect(m_prepareFrameEvent);
}
void RenderPipeline::ConnectEndFrameHandler(FrameNotificationEvent::Handler& handler)
{
handler.Connect(m_endFrameEvent);
}
void RenderPipeline::SetDrawFilterTag(RHI::DrawFilterTag tag) void RenderPipeline::SetDrawFilterTag(RHI::DrawFilterTag tag)
{ {
m_drawFilterTag = tag; m_drawFilterTag = tag;

@ -418,7 +418,7 @@ namespace AZ
} }
} }
void Scene::PrepareRender([[maybe_unused]]const TickTimeInfo& tickInfo, RHI::JobPolicy jobPolicy) void Scene::PrepareRender(const TickTimeInfo& tickInfo, RHI::JobPolicy jobPolicy)
{ {
AZ_PROFILE_SCOPE(RPI, "Scene: PrepareRender"); AZ_PROFILE_SCOPE(RPI, "Scene: PrepareRender");
@ -429,27 +429,20 @@ namespace AZ
SceneNotificationBus::Event(GetId(), &SceneNotification::OnBeginPrepareRender); SceneNotificationBus::Event(GetId(), &SceneNotification::OnBeginPrepareRender);
// Get active pipelines which need to be rendered and notify them of an impending frame. // Get active pipelines which need to be rendered and notify them frame started
AZStd::vector<RenderPipelinePtr> activePipelines; AZStd::vector<RenderPipelinePtr> activePipelines;
{ {
AZ_PROFILE_SCOPE(RPI, "Scene: OnPrepareFrame"); AZ_PROFILE_SCOPE(RPI, "Scene: OnStartFrame");
for (auto& pipeline : m_pipelines) for (auto& pipeline : m_pipelines)
{ {
pipeline->OnPrepareFrame();
if (pipeline->NeedsRender()) if (pipeline->NeedsRender())
{ {
activePipelines.push_back(pipeline); activePipelines.push_back(pipeline);
pipeline->OnStartFrame(tickInfo);
} }
} }
} }
// Get active pipelines which need to be rendered and notify them frame started
for (const auto& pipeline : activePipelines)
{
AZ_PROFILE_SCOPE(RPI, "Scene: OnStartFrame");
pipeline->OnStartFrame();
}
// Return if there is no active render pipeline // Return if there is no active render pipeline
if (activePipelines.empty()) if (activePipelines.empty())
{ {
@ -589,12 +582,10 @@ namespace AZ
void Scene::OnFrameEnd() void Scene::OnFrameEnd()
{ {
AZ_PROFILE_SCOPE(RPI, "Scene: OnFrameEnd"); AZ_PROFILE_SCOPE(RPI, "Scene: OnFrameEnd");
bool didRender = false;
for (auto& pipeline : m_pipelines) for (auto& pipeline : m_pipelines)
{ {
if (pipeline->NeedsRender()) if (pipeline->NeedsRender())
{ {
didRender = true;
pipeline->OnFrameEnd(); pipeline->OnFrameEnd();
} }
} }
@ -602,10 +593,6 @@ namespace AZ
{ {
fp->OnRenderEnd(); fp->OnRenderEnd();
} }
if (didRender)
{
SceneNotificationBus::Event(GetId(), &SceneNotification::OnFrameEnd);
}
} }
void Scene::UpdateSrgs() void Scene::UpdateSrgs()

@ -241,11 +241,8 @@ namespace AZ
{ {
AZ_PROFILE_SCOPE(RPI, "View: FinalizeDrawLists"); AZ_PROFILE_SCOPE(RPI, "View: FinalizeDrawLists");
m_drawListContext.FinalizeLists(); m_drawListContext.FinalizeLists();
if (m_passesByDrawList)
{
SortFinalizedDrawLists(); SortFinalizedDrawLists();
} }
}
void View::SortFinalizedDrawLists() void View::SortFinalizedDrawLists()
{ {

@ -25,13 +25,14 @@ namespace AZ
, m_viewportSize(1, 1) , m_viewportSize(1, 1)
{ {
m_windowContext->Initialize(device, nativeWindow); m_windowContext->Initialize(device, nativeWindow);
AzFramework::WindowRequestBus::Event(nativeWindow, [this](AzFramework::WindowRequestBus::Events* window) AzFramework::WindowRequestBus::EventResult(
{ m_viewportSize,
m_viewportSize = window->GetClientAreaSize(); nativeWindow,
m_viewportDpiScaleFactor = window->GetDpiScaleFactor(); &AzFramework::WindowRequestBus::Events::GetClientAreaSize);
m_vsyncInterval = window->GetSyncInterval(); AzFramework::WindowRequestBus::EventResult(
m_refreshRate = window->GetDisplayRefreshRate(); m_viewportDpiScaleFactor,
}); nativeWindow,
&AzFramework::WindowRequestBus::Events::GetDpiScaleFactor);
AzFramework::WindowNotificationBus::Handler::BusConnect(nativeWindow); AzFramework::WindowNotificationBus::Handler::BusConnect(nativeWindow);
AzFramework::ViewportRequestBus::Handler::BusConnect(id); AzFramework::ViewportRequestBus::Handler::BusConnect(id);
@ -45,20 +46,6 @@ namespace AZ
m_viewMatrixChangedEvent.Signal(matrix); m_viewMatrixChangedEvent.Signal(matrix);
}); });
m_prepareFrameHandler = RenderPipeline::FrameNotificationEvent::Handler(
[this]()
{
ViewportContextNotificationBus::Event(GetName(), &ViewportContextNotificationBus::Events::OnRenderTick);
ViewportContextIdNotificationBus::Event(GetId(), &ViewportContextIdNotificationBus::Events::OnRenderTick);
});
m_endFrameHandler = RenderPipeline::FrameNotificationEvent::Handler(
[this]()
{
ViewportContextNotificationBus::Event(GetName(), &ViewportContextNotificationBus::Events::OnFrameEnd);
ViewportContextIdNotificationBus::Event(GetId(), &ViewportContextIdNotificationBus::Events::OnFrameEnd);
});
SetRenderScene(renderScene); SetRenderScene(renderScene);
} }
@ -124,38 +111,26 @@ namespace AZ
{ {
SceneNotificationBus::Handler::BusConnect(m_rootScene->GetId()); SceneNotificationBus::Handler::BusConnect(m_rootScene->GetId());
} }
ResetCurrentPipeline(); m_currentPipeline.reset();
UpdatePipelineView(); UpdatePipelineView();
UpdatePipelineRefreshRate();
} }
m_sceneChangedEvent.Signal(scene); m_sceneChangedEvent.Signal(scene);
} }
float ViewportContext::GetFpsLimit() const void ViewportContext::RenderTick()
{ {
return m_fpsLimit; // add the current pipeline to next render tick if it's not already added.
} if (m_currentPipeline && m_currentPipeline->GetRenderMode() != RenderPipeline::RenderMode::RenderOnce)
void ViewportContext::SetFpsLimit(float fpsLimit)
{ {
m_fpsLimit = fpsLimit; m_currentPipeline->AddToRenderTickOnce();
UpdatePipelineRefreshRate(); }
} }
float ViewportContext::GetTargetFrameRate() const void ViewportContext::OnBeginPrepareRender()
{
float targetFrameRate = GetFpsLimit();
const AZ::u32 vsyncInterval = GetVsyncInterval();
if (vsyncInterval != 0)
{
const float vsyncFrameRate = static_cast<float>(GetRefreshRate()) / static_cast<float>(vsyncInterval);
if (targetFrameRate == 0.f || vsyncFrameRate < targetFrameRate)
{ {
targetFrameRate = vsyncFrameRate; ViewportContextNotificationBus::Event(GetName(), &ViewportContextNotificationBus::Events::OnRenderTick);
} ViewportContextIdNotificationBus::Event(GetId(), &ViewportContextIdNotificationBus::Events::OnRenderTick);
}
return targetFrameRate;
} }
AZ::Name ViewportContext::GetName() const AZ::Name ViewportContext::GetName() const
@ -183,16 +158,6 @@ namespace AZ
return m_viewportDpiScaleFactor; return m_viewportDpiScaleFactor;
} }
uint32_t ViewportContext::GetVsyncInterval() const
{
return m_vsyncInterval;
}
uint32_t ViewportContext::GetRefreshRate() const
{
return m_refreshRate;
}
void ViewportContext::ConnectSizeChangedHandler(SizeChangedEvent::Handler& handler) void ViewportContext::ConnectSizeChangedHandler(SizeChangedEvent::Handler& handler)
{ {
handler.Connect(m_sizeChangedEvent); handler.Connect(m_sizeChangedEvent);
@ -203,16 +168,6 @@ namespace AZ
handler.Connect(m_dpiScalingFactorChangedEvent); handler.Connect(m_dpiScalingFactorChangedEvent);
} }
void ViewportContext::ConnectVsyncIntervalChangedHandler(UintChangedEvent::Handler& handler)
{
handler.Connect(m_vsyncIntervalChangedEvent);
}
void ViewportContext::ConnectRefreshRateChangedHandler(UintChangedEvent::Handler& handler)
{
handler.Connect(m_refreshRateChangedEvent);
}
void ViewportContext::ConnectViewMatrixChangedHandler(MatrixChangedEvent::Handler& handler) void ViewportContext::ConnectViewMatrixChangedHandler(MatrixChangedEvent::Handler& handler)
{ {
handler.Connect(m_viewMatrixChangedEvent); handler.Connect(m_viewMatrixChangedEvent);
@ -308,43 +263,12 @@ namespace AZ
m_currentPipelineChangedEvent.Signal(m_currentPipeline); m_currentPipelineChangedEvent.Signal(m_currentPipeline);
} }
if (m_currentPipeline) if (auto pipeline = GetCurrentPipeline())
{
if (!m_prepareFrameHandler.IsConnected())
{
m_currentPipeline->ConnectPrepareFrameHandler(m_prepareFrameHandler);
m_currentPipeline->ConnectEndFrameHandler(m_endFrameHandler);
}
m_currentPipeline->SetDefaultView(m_defaultView);
}
}
void ViewportContext::UpdatePipelineRefreshRate()
{
if (!m_currentPipeline)
{
return;
}
const float refreshRate = GetTargetFrameRate();
// If we have a truly unlimited framerate, just render every tick
if (refreshRate == 0.f)
{
m_currentPipeline->AddToRenderTick();
}
else
{ {
m_currentPipeline->AddToRenderTickAtInterval(AZStd::chrono::duration<float>(1.f / refreshRate)); pipeline->SetDefaultView(m_defaultView);
} }
} }
void ViewportContext::ResetCurrentPipeline()
{
m_prepareFrameHandler.Disconnect();
m_endFrameHandler.Disconnect();
m_currentPipeline.reset();
}
RenderPipelinePtr ViewportContext::GetCurrentPipeline() RenderPipelinePtr ViewportContext::GetCurrentPipeline()
{ {
return m_currentPipeline; return m_currentPipeline;
@ -357,9 +281,8 @@ namespace AZ
// in the event prioritization is added later // in the event prioritization is added later
if (pipeline->GetWindowHandle() == m_windowContext->GetWindowHandle()) if (pipeline->GetWindowHandle() == m_windowContext->GetWindowHandle())
{ {
ResetCurrentPipeline(); m_currentPipeline.reset();
UpdatePipelineView(); UpdatePipelineView();
UpdatePipelineRefreshRate();
} }
} }
@ -367,9 +290,8 @@ namespace AZ
{ {
if (m_currentPipeline.get() == pipeline) if (m_currentPipeline.get() == pipeline)
{ {
ResetCurrentPipeline(); m_currentPipeline.reset();
UpdatePipelineView(); UpdatePipelineView();
UpdatePipelineRefreshRate();
} }
} }
@ -383,30 +305,10 @@ namespace AZ
} }
} }
void ViewportContext::OnRefreshRateChanged(uint32_t refreshRate)
{
if (m_refreshRate != refreshRate)
{
m_refreshRate = refreshRate;
m_refreshRateChangedEvent.Signal(m_refreshRate);
UpdatePipelineRefreshRate();
}
}
void ViewportContext::OnDpiScaleFactorChanged(float dpiScaleFactor) void ViewportContext::OnDpiScaleFactorChanged(float dpiScaleFactor)
{ {
m_viewportDpiScaleFactor = dpiScaleFactor; m_viewportDpiScaleFactor = dpiScaleFactor;
m_dpiScalingFactorChangedEvent.Signal(dpiScaleFactor); m_dpiScalingFactorChangedEvent.Signal(dpiScaleFactor);
} }
void ViewportContext::OnVsyncIntervalChanged(uint32_t interval)
{
if (m_vsyncInterval != interval)
{
m_vsyncInterval = interval;
m_vsyncIntervalChangedEvent.Signal(m_vsyncInterval);
UpdatePipelineRefreshRate();
}
}
} // namespace RPI } // namespace RPI
} // namespace AZ } // namespace AZ

@ -10,7 +10,6 @@
#include <QWidget> #include <QWidget>
#include <QElapsedTimer> #include <QElapsedTimer>
#include <QPointer>
#include <Atom/RPI.Public/Base.h> #include <Atom/RPI.Public/Base.h>
#include <AzToolsFramework/Viewport/ViewportMessages.h> #include <AzToolsFramework/Viewport/ViewportMessages.h>
#include <AzToolsFramework/Input/QtEventToAzInputManager.h> #include <AzToolsFramework/Input/QtEventToAzInputManager.h>
@ -22,8 +21,6 @@
#include <AzFramework/Windowing/WindowBus.h> #include <AzFramework/Windowing/WindowBus.h>
#include <AzCore/Component/TickBus.h> #include <AzCore/Component/TickBus.h>
#include <Atom/RPI.Public/AuxGeom/AuxGeomFeatureProcessorInterface.h> #include <Atom/RPI.Public/AuxGeom/AuxGeomFeatureProcessorInterface.h>
#include <Atom/Bootstrap/BootstrapNotificationBus.h>
#include <AtomToolsFramework/Viewport/RenderViewportWidgetNotificationBus.h>
namespace AtomToolsFramework namespace AtomToolsFramework
{ {
@ -38,8 +35,6 @@ namespace AtomToolsFramework
, public AzFramework::WindowRequestBus::Handler , public AzFramework::WindowRequestBus::Handler
, protected AzFramework::InputChannelEventListener , protected AzFramework::InputChannelEventListener
, protected AZ::TickBus::Handler , protected AZ::TickBus::Handler
, protected AZ::Render::Bootstrap::NotificationBus::Handler
, protected AtomToolsFramework::RenderViewportWidgetNotificationBus::Handler
{ {
public: public:
//! Creates a RenderViewportWidget. //! Creates a RenderViewportWidget.
@ -126,7 +121,6 @@ namespace AtomToolsFramework
// AZ::TickBus::Handler ... // AZ::TickBus::Handler ...
void OnTick(float deltaTime, AZ::ScriptTimePoint time) override; void OnTick(float deltaTime, AZ::ScriptTimePoint time) override;
int GetTickOrder() override;
// QWidget ... // QWidget ...
void resizeEvent(QResizeEvent *event) override; void resizeEvent(QResizeEvent *event) override;
@ -134,21 +128,9 @@ namespace AtomToolsFramework
void enterEvent(QEvent* event) override; void enterEvent(QEvent* event) override;
void leaveEvent(QEvent* event) override; void leaveEvent(QEvent* event) override;
void mouseMoveEvent(QMouseEvent* event) override; void mouseMoveEvent(QMouseEvent* event) override;
void focusInEvent(QFocusEvent* event) override;
// AZ::Render::Bootstrap::NotificationBus::Handler ...
void OnFrameRateLimitChanged(float fpsLimit) override;
// AtomToolsFramework::RenderViewportWidgetNotificationBus::Handler ...
void OnInactiveViewportFrameRateChanged(float fpsLimit) override;
private: private:
AzFramework::NativeWindowHandle GetNativeWindowHandle() const;
void UpdateFrameRate();
void SetScreen(QScreen* screen);
void SendWindowResizeEvent(); void SendWindowResizeEvent();
void NotifyUpdateRefreshRate();
// The underlying ViewportContext, our entry-point to the Atom RPI. // The underlying ViewportContext, our entry-point to the Atom RPI.
AZ::RPI::ViewportContextPtr m_viewportContext; AZ::RPI::ViewportContextPtr m_viewportContext;
@ -169,11 +151,5 @@ namespace AtomToolsFramework
AZ::ScriptTimePoint m_time; AZ::ScriptTimePoint m_time;
// Maps our internal Qt events into AzFramework InputChannels for our ViewportControllerList. // Maps our internal Qt events into AzFramework InputChannels for our ViewportControllerList.
AzToolsFramework::QtEventToAzInputMapper* m_inputChannelMapper = nullptr; AzToolsFramework::QtEventToAzInputMapper* m_inputChannelMapper = nullptr;
// Stores our current screen, used for tracking the current refresh rate.
QScreen* m_screen = nullptr;
// Stores the last RenderViewportWidget that has received user focus.
// This is used for optional framerate throtting for "inactive" viewports via the
// ed_inactive_viewport_fps_limit CVAR.
AZ::EnvironmentVariable<RenderViewportWidget*> m_lastFocusedViewport;
}; };
} //namespace AtomToolsFramework } //namespace AtomToolsFramework

@ -1,35 +0,0 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#include <AzCore/EBus/EBus.h>
namespace AtomToolsFramework
{
//! Provides an interface for providing notifications specific to RenderViewportWidget.
//! @note Most behaviors in RenderViewportWidget are handled by its underyling
//! ViewportContext, this bus is specifically for functionality exclusive to the
//! Qt layer provided by RenderViewportWidget.
class RenderViewportWidgetNotifications : public AZ::EBusTraits
{
public:
static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single;
static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Multiple;
//! Triggered when the idle frame rate limit for inactive viewports changed.
//! Controlled by the ed_inactive_viewport_fps_limit CVAR.
//! Active viewports are controlled by the r_fps_limit CVAR.
virtual void OnInactiveViewportFrameRateChanged([[maybe_unused]]float fpsLimit){}
protected:
~RenderViewportWidgetNotifications() = default;
};
using RenderViewportWidgetNotificationBus = AZ::EBus<RenderViewportWidgetNotifications>;
} // namespace AtomToolsFramework

@ -6,42 +6,23 @@
* *
*/ */
#include <Atom/Bootstrap/BootstrapRequestBus.h> #include <AtomToolsFramework/Viewport/RenderViewportWidget.h>
#include <Atom/RHI/RHISystemInterface.h>
#include <Atom/RPI.Public/View.h>
#include <Atom/RPI.Public/ViewportContext.h> #include <Atom/RPI.Public/ViewportContext.h>
#include <Atom/RPI.Public/ViewportContextBus.h> #include <Atom/RPI.Public/ViewportContextBus.h>
#include <AtomToolsFramework/Viewport/RenderViewportWidget.h> #include <Atom/RPI.Public/View.h>
#include <AzCore/Console/Console.h>
#include <AzCore/Math/MathUtils.h>
#include <AzFramework/Input/Devices/Mouse/InputDeviceMouse.h> #include <AzFramework/Input/Devices/Mouse/InputDeviceMouse.h>
#include <AzFramework/Viewport/ViewportControllerList.h> #include <AzFramework/Viewport/ViewportControllerList.h>
#include <AzFramework/Viewport/ViewportScreen.h> #include <AzFramework/Viewport/ViewportScreen.h>
#include <AzToolsFramework/Viewport/ViewportTypes.h> #include <AzToolsFramework/Viewport/ViewportTypes.h>
#include <AzCore/Math/MathUtils.h>
#include <Atom/RHI/RHISystemInterface.h>
#include <Atom/Bootstrap/BootstrapRequestBus.h>
#include <QApplication> #include <QApplication>
#include <QBoxLayout>
#include <QCursor> #include <QCursor>
#include <QMouseEvent> #include <QBoxLayout>
#include <QScreen>
#include <QTimer>
#include <QWindow> #include <QWindow>
#include <QMouseEvent>
static void OnInactiveViewportFrameRateChanged(const float& fpsLimit)
{
AtomToolsFramework::RenderViewportWidgetNotificationBus::Broadcast(
&AtomToolsFramework::RenderViewportWidgetNotificationBus::Events::OnInactiveViewportFrameRateChanged, fpsLimit);
}
AZ_CVAR(
float,
ed_inactive_viewport_fps_limit,
0,
OnInactiveViewportFrameRateChanged,
AZ::ConsoleFunctorFlags::Null,
"The maximum framerate to render viewports that don't have focus at");
static constexpr const char* LastFocusedViewportVariableName = "AtomToolsFramework::RenderViewportWidget::LastFocusedViewport";
namespace AtomToolsFramework namespace AtomToolsFramework
{ {
@ -49,12 +30,6 @@ namespace AtomToolsFramework
: QWidget(parent) : QWidget(parent)
, AzFramework::InputChannelEventListener(AzFramework::InputChannelEventListener::GetPriorityDefault()) , AzFramework::InputChannelEventListener(AzFramework::InputChannelEventListener::GetPriorityDefault())
{ {
m_lastFocusedViewport = AZ::Environment::FindVariable<RenderViewportWidget*>(LastFocusedViewportVariableName);
if (!m_lastFocusedViewport)
{
m_lastFocusedViewport = AZ::Environment::CreateVariable<RenderViewportWidget*>(LastFocusedViewportVariableName, nullptr);
}
if (shouldInitializeViewportContext) if (shouldInitializeViewportContext)
{ {
InitializeViewportContext(); InitializeViewportContext();
@ -63,24 +38,13 @@ namespace AtomToolsFramework
setUpdatesEnabled(false); setUpdatesEnabled(false);
setFocusPolicy(Qt::FocusPolicy::WheelFocus); setFocusPolicy(Qt::FocusPolicy::WheelFocus);
setMouseTracking(true); setMouseTracking(true);
// Wait a frame for our window handle to be constructed, then wire up our screen change signals.
QTimer::singleShot(
0,
[this]()
{
QObject::connect(windowHandle(), &QWindow::screenChanged, this, &RenderViewportWidget::SetScreen);
});
SetScreen(screen());
} }
bool RenderViewportWidget::InitializeViewportContext(AzFramework::ViewportId id) bool RenderViewportWidget::InitializeViewportContext(AzFramework::ViewportId id)
{ {
if (m_viewportContext != nullptr) if (m_viewportContext != nullptr)
{ {
AZ_Assert( AZ_Assert(id == AzFramework::InvalidViewportId || m_viewportContext->GetId() == id, "Attempted to reinitialize RenderViewportWidget with a different ID");
id == AzFramework::InvalidViewportId || m_viewportContext->GetId() == id,
"Attempted to reinitialize RenderViewportWidget with a different ID");
return true; return true;
} }
@ -95,7 +59,7 @@ namespace AtomToolsFramework
// Before we do anything else, we must create a ViewportContext which will give us a ViewportId if we didn't manually specify one. // Before we do anything else, we must create a ViewportContext which will give us a ViewportId if we didn't manually specify one.
AZ::RPI::ViewportContextRequestsInterface::CreationParameters params; AZ::RPI::ViewportContextRequestsInterface::CreationParameters params;
params.device = AZ::RHI::RHISystemInterface::Get()->GetDevice(); params.device = AZ::RHI::RHISystemInterface::Get()->GetDevice();
params.windowHandle = GetNativeWindowHandle(); params.windowHandle = reinterpret_cast<AzFramework::NativeWindowHandle>(winId());
params.id = id; params.id = id;
AzFramework::WindowRequestBus::Handler::BusConnect(params.windowHandle); AzFramework::WindowRequestBus::Handler::BusConnect(params.windowHandle);
m_viewportContext = viewportContextManager->CreateViewportContext(AZ::Name(), params); m_viewportContext = viewportContextManager->CreateViewportContext(AZ::Name(), params);
@ -116,19 +80,15 @@ namespace AtomToolsFramework
AzFramework::InputChannelEventListener::Connect(); AzFramework::InputChannelEventListener::Connect();
AZ::TickBus::Handler::BusConnect(); AZ::TickBus::Handler::BusConnect();
AzFramework::WindowRequestBus::Handler::BusConnect(params.windowHandle); AzFramework::WindowRequestBus::Handler::BusConnect(params.windowHandle);
AZ::Render::Bootstrap::NotificationBus::Handler::BusConnect();
AtomToolsFramework::RenderViewportWidgetNotificationBus::Handler::BusConnect();
m_inputChannelMapper = new AzToolsFramework::QtEventToAzInputMapper(this, id); m_inputChannelMapper = new AzToolsFramework::QtEventToAzInputMapper(this, id);
// Forward input events to our controller list. // Forward input events to our controller list.
QObject::connect( QObject::connect(m_inputChannelMapper, &AzToolsFramework::QtEventToAzInputMapper::InputChannelUpdated, this,
m_inputChannelMapper, &AzToolsFramework::QtEventToAzInputMapper::InputChannelUpdated, this,
[this](const AzFramework::InputChannel* inputChannel, QEvent* event) [this](const AzFramework::InputChannel* inputChannel, QEvent* event)
{ {
const AzFramework::NativeWindowHandle windowId = GetNativeWindowHandle(); AzFramework::NativeWindowHandle windowId = reinterpret_cast<AzFramework::NativeWindowHandle>(winId());
if (m_controllerList->HandleInputChannelEvent( if (m_controllerList->HandleInputChannelEvent(AzFramework::ViewportControllerInputEvent{GetId(), windowId, *inputChannel}))
AzFramework::ViewportControllerInputEvent{ GetId(), windowId, *inputChannel }))
{ {
// If the controller handled the input event, mark the event as accepted so it doesn't continue to propagate. // If the controller handled the input event, mark the event as accepted so it doesn't continue to propagate.
if (event) if (event)
@ -138,24 +98,11 @@ namespace AtomToolsFramework
} }
}); });
// Update our target frame rate. If we're the only viewport, become active.
if (m_lastFocusedViewport.Get() == nullptr)
{
m_lastFocusedViewport.Set(this);
}
UpdateFrameRate();
return true; return true;
} }
RenderViewportWidget::~RenderViewportWidget() RenderViewportWidget::~RenderViewportWidget()
{ {
if (m_lastFocusedViewport.Get() == this)
{
m_lastFocusedViewport.Set(nullptr);
}
AzFramework::WindowRequestBus::Handler::BusDisconnect(); AzFramework::WindowRequestBus::Handler::BusDisconnect();
AZ::TickBus::Handler::BusDisconnect(); AZ::TickBus::Handler::BusDisconnect();
AzFramework::InputChannelEventListener::Disconnect(); AzFramework::InputChannelEventListener::Disconnect();
@ -234,10 +181,10 @@ namespace AtomToolsFramework
bool shouldConsumeEvent = true; bool shouldConsumeEvent = true;
const bool eventHandled = m_controllerList->HandleInputChannelEvent({ GetId(), GetNativeWindowHandle(), inputChannel }); AzFramework::NativeWindowHandle windowId = reinterpret_cast<AzFramework::NativeWindowHandle>(winId());
const bool eventHandled = m_controllerList->HandleInputChannelEvent({GetId(), windowId, inputChannel});
// If our controllers handled the event and it's one we can safely consume (i.e. it's not an Ended event that other viewports might // If our controllers handled the event and it's one we can safely consume (i.e. it's not an Ended event that other viewports might need), consume it.
// need), consume it.
return eventHandled && shouldConsumeEvent; return eventHandled && shouldConsumeEvent;
} }
@ -247,11 +194,6 @@ namespace AtomToolsFramework
m_controllerList->UpdateViewport({GetId(), AzFramework::FloatSeconds(deltaTime), m_time}); m_controllerList->UpdateViewport({GetId(), AzFramework::FloatSeconds(deltaTime), m_time});
} }
int RenderViewportWidget::GetTickOrder()
{
return AZ::ComponentTickBus::TICK_PRE_RENDER;
}
void RenderViewportWidget::resizeEvent([[maybe_unused]] QResizeEvent* event) void RenderViewportWidget::resizeEvent([[maybe_unused]] QResizeEvent* event)
{ {
SendWindowResizeEvent(); SendWindowResizeEvent();
@ -277,75 +219,6 @@ namespace AtomToolsFramework
m_mousePosition = event->localPos(); m_mousePosition = event->localPos();
} }
void RenderViewportWidget::focusInEvent([[maybe_unused]] QFocusEvent* event)
{
RenderViewportWidget* lastFocusedViewport = m_lastFocusedViewport.Get();
if (lastFocusedViewport == this)
{
return;
}
RenderViewportWidget* previousFocusWidget = lastFocusedViewport;
m_lastFocusedViewport.Set(this);
// Ensure this viewport and whatever viewport last had focus (if any) respect
// the active / inactive viewport frame rate settings.
UpdateFrameRate();
if (previousFocusWidget != nullptr)
{
previousFocusWidget->UpdateFrameRate();
}
}
void RenderViewportWidget::OnFrameRateLimitChanged([[maybe_unused]] float fpsLimit)
{
UpdateFrameRate();
}
void RenderViewportWidget::OnInactiveViewportFrameRateChanged([[maybe_unused]] float fpsLimit)
{
UpdateFrameRate();
}
AzFramework::NativeWindowHandle RenderViewportWidget::GetNativeWindowHandle() const
{
return reinterpret_cast<AzFramework::NativeWindowHandle>(winId());
}
void RenderViewportWidget::UpdateFrameRate()
{
if (ed_inactive_viewport_fps_limit > 0.f && m_lastFocusedViewport.Get() != this)
{
m_viewportContext->SetFpsLimit(ed_inactive_viewport_fps_limit);
}
else
{
float fpsLimit = 0.f;
AZ::Render::Bootstrap::RequestBus::BroadcastResult(fpsLimit, &AZ::Render::Bootstrap::RequestBus::Events::GetFrameRateLimit);
m_viewportContext->SetFpsLimit(fpsLimit);
}
}
void RenderViewportWidget::SetScreen(QScreen* screen)
{
if (m_screen != screen)
{
if (m_screen)
{
QObject::disconnect(m_screen, &QScreen::refreshRateChanged, this, &RenderViewportWidget::NotifyUpdateRefreshRate);
}
if (screen)
{
QObject::connect(m_screen, &QScreen::refreshRateChanged, this, &RenderViewportWidget::NotifyUpdateRefreshRate);
}
NotifyUpdateRefreshRate();
m_screen = screen;
}
}
void RenderViewportWidget::SendWindowResizeEvent() void RenderViewportWidget::SendWindowResizeEvent()
{ {
// Scale the size by the DPI of the platform to // Scale the size by the DPI of the platform to
@ -353,14 +226,8 @@ namespace AtomToolsFramework
const QSize uiWindowSize = size(); const QSize uiWindowSize = size();
const QSize windowSize = uiWindowSize * devicePixelRatioF(); const QSize windowSize = uiWindowSize * devicePixelRatioF();
AzFramework::WindowNotificationBus::Event( const AzFramework::NativeWindowHandle windowId = reinterpret_cast<AzFramework::NativeWindowHandle>(winId());
GetNativeWindowHandle(), &AzFramework::WindowNotifications::OnWindowResized, windowSize.width(), windowSize.height()); AzFramework::WindowNotificationBus::Event(windowId, &AzFramework::WindowNotifications::OnWindowResized, windowSize.width(), windowSize.height());
}
void RenderViewportWidget::NotifyUpdateRefreshRate()
{
AzFramework::WindowNotificationBus::Event(
GetNativeWindowHandle(), &AzFramework::WindowNotificationBus::Events::OnRefreshRateChanged, GetDisplayRefreshRate());
} }
AZ::Name RenderViewportWidget::GetCurrentContextName() const AZ::Name RenderViewportWidget::GetCurrentContextName() const
@ -418,7 +285,9 @@ namespace AtomToolsFramework
// Build camera state from Atom camera transforms // Build camera state from Atom camera transforms
AzFramework::CameraState cameraState = AzFramework::CreateCameraFromWorldFromViewMatrix( AzFramework::CameraState cameraState = AzFramework::CreateCameraFromWorldFromViewMatrix(
currentView->GetViewToWorldMatrix(), AZ::Vector2{ aznumeric_cast<float>(width()), aznumeric_cast<float>(height()) }); currentView->GetViewToWorldMatrix(),
AZ::Vector2{aznumeric_cast<float>(width()), aznumeric_cast<float>(height())}
);
AzFramework::SetCameraClippingVolumeFromPerspectiveFovMatrixRH(cameraState, currentView->GetViewToClipMatrix()); AzFramework::SetCameraClippingVolumeFromPerspectiveFovMatrixRH(cameraState, currentView->GetViewToClipMatrix());
// Convert from Z-up // Convert from Z-up
@ -430,7 +299,8 @@ namespace AtomToolsFramework
AzFramework::ScreenPoint RenderViewportWidget::ViewportWorldToScreen(const AZ::Vector3& worldPosition) AzFramework::ScreenPoint RenderViewportWidget::ViewportWorldToScreen(const AZ::Vector3& worldPosition)
{ {
if (AZ::RPI::ViewPtr currentView = m_viewportContext->GetDefaultView(); currentView == nullptr) if (AZ::RPI::ViewPtr currentView = m_viewportContext->GetDefaultView();
currentView == nullptr)
{ {
return AzFramework::ScreenPoint(0, 0); return AzFramework::ScreenPoint(0, 0);
} }
@ -443,10 +313,12 @@ namespace AtomToolsFramework
const auto& cameraProjection = m_viewportContext->GetCameraProjectionMatrix(); const auto& cameraProjection = m_viewportContext->GetCameraProjectionMatrix();
const auto& cameraView = m_viewportContext->GetCameraViewMatrix(); const auto& cameraView = m_viewportContext->GetCameraViewMatrix();
const AZ::Vector4 normalizedScreenPosition{ screenPosition.m_x * 2.f / width() - 1.0f, const AZ::Vector4 normalizedScreenPosition {
screenPosition.m_x * 2.f / width() - 1.0f,
(height() - screenPosition.m_y) * 2.f / height() - 1.0f, (height() - screenPosition.m_y) * 2.f / height() - 1.0f,
1.f - depth, // [GFX TODO] [ATOM-1501] Currently we always assume reverse depth 1.f - depth, // [GFX TODO] [ATOM-1501] Currently we always assume reverse depth
1.f }; 1.f
};
AZ::Matrix4x4 worldFromScreen = cameraProjection * cameraView; AZ::Matrix4x4 worldFromScreen = cameraProjection * cameraView;
worldFromScreen.InvertFull(); worldFromScreen.InvertFull();
@ -543,20 +415,11 @@ namespace AtomToolsFramework
uint32_t RenderViewportWidget::GetDisplayRefreshRate() const uint32_t RenderViewportWidget::GetDisplayRefreshRate() const
{ {
return static_cast<uint32_t>(screen()->refreshRate()); return 60;
} }
uint32_t RenderViewportWidget::GetSyncInterval() const uint32_t RenderViewportWidget::GetSyncInterval() const
{ {
uint32_t interval = 1; return 1;
// Get vsync_interval from AzFramework::NativeWindow, which owns it.
// NativeWindow also handles broadcasting OnVsyncIntervalChanged to all
// WindowNotificationBus listeners.
if (auto console = AZ::Interface<AZ::IConsole>::Get())
{
console->GetCvarValue<uint32_t>("vsync_interval", interval);
}
return interval;
} }
} //namespace AtomToolsFramework } //namespace AtomToolsFramework

@ -28,7 +28,6 @@ set(FILES
Include/AtomToolsFramework/Util/MaterialPropertyUtil.h Include/AtomToolsFramework/Util/MaterialPropertyUtil.h
Include/AtomToolsFramework/Util/Util.h Include/AtomToolsFramework/Util/Util.h
Include/AtomToolsFramework/Viewport/RenderViewportWidget.h Include/AtomToolsFramework/Viewport/RenderViewportWidget.h
Include/AtomToolsFramework/Viewport/RenderViewportWidgetNotificationBus.h
Include/AtomToolsFramework/Viewport/ModularViewportCameraController.h Include/AtomToolsFramework/Viewport/ModularViewportCameraController.h
Include/AtomToolsFramework/Viewport/ModularViewportCameraControllerRequestBus.h Include/AtomToolsFramework/Viewport/ModularViewportCameraControllerRequestBus.h
Include/AtomToolsFramework/Window/AtomToolsMainWindow.h Include/AtomToolsFramework/Window/AtomToolsMainWindow.h

@ -183,15 +183,6 @@ namespace AZ::Render
DrawFramerate(); DrawFramerate();
} }
void AtomViewportDisplayInfoSystemComponent::OnFrameEnd()
{
auto currentTime = AZStd::chrono::system_clock::now();
if (!m_fpsHistory.empty())
{
m_fpsHistory.back().m_endFrameTime = currentTime;
}
}
AtomBridge::ViewportInfoDisplayState AtomViewportDisplayInfoSystemComponent::GetDisplayState() const AtomBridge::ViewportInfoDisplayState AtomViewportDisplayInfoSystemComponent::GetDisplayState() const
{ {
return aznumeric_cast<AtomBridge::ViewportInfoDisplayState>(r_displayInfo.operator int()); return aznumeric_cast<AtomBridge::ViewportInfoDisplayState>(r_displayInfo.operator int());
@ -257,11 +248,11 @@ namespace AZ::Render
void AtomViewportDisplayInfoSystemComponent::UpdateFramerate() void AtomViewportDisplayInfoSystemComponent::UpdateFramerate()
{ {
auto currentTime = AZStd::chrono::system_clock::now(); auto currentTime = AZStd::chrono::system_clock::now();
while (!m_fpsHistory.empty() && (currentTime - m_fpsHistory.front().m_beginFrameTime) > m_fpsInterval) while (!m_fpsHistory.empty() && (currentTime - m_fpsHistory.front()) > m_fpsInterval)
{ {
m_fpsHistory.pop_front(); m_fpsHistory.pop_front();
} }
m_fpsHistory.push_back(FrameTimingInfo(currentTime)); m_fpsHistory.push_back(currentTime);
} }
void AtomViewportDisplayInfoSystemComponent::DrawFramerate() void AtomViewportDisplayInfoSystemComponent::DrawFramerate()
@ -270,31 +261,25 @@ namespace AZ::Render
double minFPS = DBL_MAX; double minFPS = DBL_MAX;
double maxFPS = 0; double maxFPS = 0;
AZStd::chrono::duration<double> deltaTime; AZStd::chrono::duration<double> deltaTime;
AZStd::chrono::milliseconds totalFrameMS(0);
for (const auto& time : m_fpsHistory) for (const auto& time : m_fpsHistory)
{ {
if (lastTime.has_value()) if (lastTime.has_value())
{ {
deltaTime = time.m_beginFrameTime - lastTime.value(); deltaTime = time - lastTime.value();
double fps = AZStd::chrono::seconds(1) / deltaTime; double fps = AZStd::chrono::seconds(1) / deltaTime;
minFPS = AZStd::min(minFPS, fps); minFPS = AZStd::min(minFPS, fps);
maxFPS = AZStd::max(maxFPS, fps); maxFPS = AZStd::max(maxFPS, fps);
} }
lastTime = time.m_beginFrameTime; lastTime = time;
if (time.m_endFrameTime.has_value())
{
totalFrameMS += time.m_endFrameTime.value() - time.m_beginFrameTime;
}
} }
double averageFPS = 0; double averageFPS = 0;
double averageFrameMs = 0; double averageFrameMs = 0;
if (m_fpsHistory.size() > 1) if (m_fpsHistory.size() > 1)
{ {
deltaTime = m_fpsHistory.back().m_beginFrameTime - m_fpsHistory.front().m_beginFrameTime; deltaTime = m_fpsHistory.back() - m_fpsHistory.front();
averageFPS = AZStd::chrono::seconds(m_fpsHistory.size() - 1) / deltaTime; averageFPS = AZStd::chrono::seconds(m_fpsHistory.size()) / deltaTime;
averageFrameMs = aznumeric_cast<double>(totalFrameMS.count()) / (m_fpsHistory.size() - 1); averageFrameMs = 1000.0f/averageFPS;
} }
const double frameIntervalSeconds = m_fpsInterval.count(); const double frameIntervalSeconds = m_fpsInterval.count();
@ -303,7 +288,7 @@ namespace AZ::Render
AZStd::string::format( AZStd::string::format(
"FPS %.1f [%.0f..%.0f], %.1fms/frame, avg over %.1fs", "FPS %.1f [%.0f..%.0f], %.1fms/frame, avg over %.1fs",
averageFPS, averageFPS,
minFPS == DBL_MAX ? 0.0 : minFPS, minFPS,
maxFPS, maxFPS,
averageFrameMs, averageFrameMs,
frameIntervalSeconds), frameIntervalSeconds),

@ -45,7 +45,6 @@ namespace AZ
// AZ::RPI::ViewportContextNotificationBus::Handler overrides... // AZ::RPI::ViewportContextNotificationBus::Handler overrides...
void OnRenderTick() override; void OnRenderTick() override;
void OnFrameEnd() override;
// AZ::AtomBridge::AtomViewportInfoDisplayRequestBus::Handler overrides... // AZ::AtomBridge::AtomViewportInfoDisplayRequestBus::Handler overrides...
AtomBridge::ViewportInfoDisplayState GetDisplayState() const override; AtomBridge::ViewportInfoDisplayState GetDisplayState() const override;
@ -62,8 +61,6 @@ namespace AZ
void DrawPassInfo(); void DrawPassInfo();
void DrawFramerate(); void DrawFramerate();
void UpdateScene(AZ::RPI::ScenePtr scene);
static constexpr float BaseFontSize = 0.7f; static constexpr float BaseFontSize = 0.7f;
AZStd::string m_rendererDescription; AZStd::string m_rendererDescription;
@ -71,17 +68,7 @@ namespace AZ
AzFramework::FontDrawInterface* m_fontDrawInterface = nullptr; AzFramework::FontDrawInterface* m_fontDrawInterface = nullptr;
float m_lineSpacing; float m_lineSpacing;
AZStd::chrono::duration<double> m_fpsInterval = AZStd::chrono::seconds(1); AZStd::chrono::duration<double> m_fpsInterval = AZStd::chrono::seconds(1);
struct FrameTimingInfo AZStd::deque<AZStd::chrono::system_clock::time_point> m_fpsHistory;
{
AZStd::chrono::system_clock::time_point m_beginFrameTime;
AZStd::optional<AZStd::chrono::system_clock::time_point> m_endFrameTime;
explicit FrameTimingInfo(AZStd::chrono::system_clock::time_point beginFrameTime)
: m_beginFrameTime(beginFrameTime)
{
}
};
AZStd::deque<FrameTimingInfo> m_fpsHistory;
AZStd::optional<AZStd::chrono::system_clock::time_point> m_lastMemoryUpdate; AZStd::optional<AZStd::chrono::system_clock::time_point> m_lastMemoryUpdate;
bool m_updateRootPassQuery = true; bool m_updateRootPassQuery = true;
}; };

Loading…
Cancel
Save