#include <FrameGraphCompiler.h>
Inherits AZ::RHI::DeviceObject.
Inherited by AZ::DX12::FrameGraphCompiler, AZ::Metal::FrameGraphCompiler, AZ::Null::FrameGraphCompiler, and AZ::Vulkan::FrameGraphCompiler.
Public Member Functions | |
AZ_RTTI (FrameGraphCompiler, "{A126F362-C163-432E-94DE-61AA4DFDF102}", Object) | |
ResultCode | Init (Device &device) |
void | Shutdown () override final |
Clears the current bound device to null. | |
MessageOutcome | Compile (const FrameGraphCompileRequest &request) |
Public Member Functions inherited from AZ::RHI::DeviceObject | |
AZ_RTTI (DeviceObject, "{17D34F71-944C-4AF5-9823-627474C4C0A6}", Object) | |
bool | IsInitialized () const |
Returns whether the device object is initialized. | |
Device & | GetDevice () const |
Public Member Functions inherited from AZ::RHI::Object | |
AZ_RTTI (Object, "{E43378F1-2331-4173-94B8-990ED20E6003}") | |
void | SetName (const Name &name) |
Sets the name of the object. | |
const Name & | GetName () const |
Returns the name set on the object by SetName. | |
uint32_t | use_count () |
Returns the current use count of the object. | |
Additional Inherited Members | |
Protected Member Functions inherited from AZ::RHI::DeviceObject | |
void | Init (Device &device) |
The derived class should call this method to assign the device. | |
void | Shutdown () override |
Clears the current bound device to null. | |
Protected Member Functions inherited from AZ::RHI::Object | |
void | add_ref () const |
void | release () const |
Protected Attributes inherited from AZ::RHI::Object | |
AZStd::atomic_int | m_useCount = 0 |
FrameGraphCompiler controls compilation of FrameGraph each frame. FrameScheduler owns and drives an instance of this class, so end-users should never need to interact with it directly. Platform implementations, on the other hand, are required to override this class in order to perform platform-specific scope construction.
The compiler is designed to be invoked every frame; the graph is simply rebuilt each time. The compile operation is also done on a single thread; so overhead should be kept to a minimum.
The RHI base class performs platform-independent compilation before passing control down to the derived platform implementation. The provided FrameGraph instance is compiled in-place according to the following phases:
== Cross-Queue Graph Edges ==
FrameGraph contains a graph of Scope instances. Scopes are topologically sorted prior to compilation as part of the graph construction process. Scopes associate directly to a "Hardware Queue Class": Graphics, Compute, or Copy. These three queue classes must be synchronized between each other. To make this easier on platforms, the base compiler takes the topologically flattened graph and collates it into three independent sorted lists–one for each queue class. Then, a queue-centric producer-consumer graph is constructed across the scopes. Specifically:
class Scope { Scope* m_producersByQueue[HardwareQueueClassCount]; Scope* m_consumersByQueue[HardwareQueueClassCount]; };
This graph makes it possible to walk along a queue or across queue boundaries at dependency points. Each platform can then trivially crawl this graph to derive signal / wait fence values, if applicable.
== Transient Attachments ==
Transient attachments are intra-frame and do not persist after the frame ends and can take the form of buffers or images. These attachments are owned by a TransientAttachmentPool; every frame, the pool is reset. Since attachments are always declared for usage on scopes, its full usage chain–and thus its lifetime across the frame–is immediately available.
The phase first constructs the scope lifetime for each attachment. Then, memory for each attachment is allocated from the transient attachment pool, one scope at a time. This allows the pool to record begin and end usages for each attachment per scope. Internally, the platform implementation can use this information to place aliased resources onto one or more heaps of memory.
One important consideration is dealing with aliasing across queue boundaries. Since queues must be synchronized between each other, attempting to alias memory across two queues at the same time will produce a race condition. To solve this, when faced with a queue overlap, the compiler extends the lifetime of the attachment until a join operation occurs. However, the compiler picks a single queue which is allowed to alias during that region by inspecting which one will see the biggest potential gain. This way, some aliasing is still allowed when async compute / copy is in use.
Finally, because the resources themselves are effectively re-created each frame, a cache of views is kept inside the compiler. The cache is big enough to avoid having to re-create views every frame, but bounded in order to release entries old views.
== Platform-Specific Compilation ==
Finally, the compiler calls into the platform-specific compile method, which hands control over to the derived class. The platform implementation is expected to further process the frame graph and scope data down to platform-specific actions. For example:
1) Derive transition barriers by walking the scope attachment chain on each frame attachment. 2) Derive queue fence values by walking the queue-centric scope graph.
MessageOutcome AZ::RHI::FrameGraphCompiler::Compile | ( | const FrameGraphCompileRequest & | request | ) |
Compiles the frame graph. Platform-independent compilation is done first according to the provided flags. At the end, the platform-dependent compilation method is invoked.
|
finaloverridevirtual |
Clears the current bound device to null.
Reimplemented from AZ::RHI::DeviceObject.