diff --git a/generator/vk_layer/source/device.cpp b/generator/vk_layer/source/device.cpp index 580e339..3371cff 100644 --- a/generator/vk_layer/source/device.cpp +++ b/generator/vk_layer/source/device.cpp @@ -34,6 +34,9 @@ #include "device.hpp" #include "instance.hpp" +/** + * @brief The dispatch lookup for all of the created Vulkan instances. + */ static std::unordered_map> g_devices; /* See header for documentation. */ @@ -47,8 +50,8 @@ void Device::store( /* See header for documentation. */ Device* Device::retrieve( - VkDevice handle) -{ + VkDevice handle +) { void* key = getDispatchKey(handle); assert(isInMap(key, g_devices)); return g_devices.at(key).get(); @@ -56,8 +59,8 @@ Device* Device::retrieve( /* See header for documentation. */ Device* Device::retrieve( - VkQueue handle) -{ + VkQueue handle +) { void* key = getDispatchKey(handle); assert(isInMap(key, g_devices)); return g_devices.at(key).get(); @@ -65,8 +68,8 @@ Device* Device::retrieve( /* See header for documentation. */ Device* Device::retrieve( - VkCommandBuffer handle) -{ + VkCommandBuffer handle +) { void* key = getDispatchKey(handle); assert(isInMap(key, g_devices)); return g_devices.at(key).get(); @@ -85,15 +88,10 @@ Device::Device( VkPhysicalDevice _physicalDevice, VkDevice _device, PFN_vkGetDeviceProcAddr nlayerGetProcAddress -): instance(_instance), +): + instance(_instance), physicalDevice(_physicalDevice), device(_device) { initDriverDeviceDispatchTable(device, nlayerGetProcAddress, driver); } - -/* See header for documentation. */ -Device::~Device() -{ - -} diff --git a/generator/vk_layer/source/device.hpp b/generator/vk_layer/source/device.hpp index d6ecad7..c0e1f0a 100644 --- a/generator/vk_layer/source/device.hpp +++ b/generator/vk_layer/source/device.hpp @@ -24,8 +24,7 @@ */ /** - * @file - * Declares the root class for layer management of VkDevice objects. + * @file Declares the root class for layer management of VkDevice objects. * * Role summary * ============ @@ -41,10 +40,9 @@ * Key properties * ============== * - * Unlike EGL contexts, Vulkan devices are designed to be used concurrently by - * multiple application threads. An application can have multiple concurrent - * devices (although this is less common than with OpenGL ES applications), and - * use each device from multiple threads. + * Vulkan devices are designed to be used concurrently by multiple application + * threads. An application can have multiple concurrent devices, and use each + * device from multiple threads. * * Access to the layer driver structures must therefore be kept thread-safe. * For sake of simplicity, we generally implement this by: @@ -80,6 +78,8 @@ class Device * @brief Fetch a device from the global store of dispatchable devices. * * @param handle The dispatchable device handle to use as an indirect lookup. + * + * @return The layer device context. */ static Device* retrieve( VkDevice handle); @@ -88,6 +88,8 @@ class Device * @brief Fetch a device from the global store of dispatchable devices. * * @param handle The dispatchable queue handle to use as an indirect lookup. + * + * @return The layer device context. */ static Device* retrieve( VkQueue handle); @@ -96,6 +98,8 @@ class Device * @brief Fetch a device from the global store of dispatchable devices. * * @param handle The dispatchable command buffer handle to use as an indirect lookup. + * + * @return The layer device context. */ static Device* retrieve( VkCommandBuffer handle); @@ -125,7 +129,7 @@ class Device /** * @brief Destroy this layer device object. */ - ~Device(); + ~Device() = default; public: /** diff --git a/generator/vk_layer/source/instance.cpp b/generator/vk_layer/source/instance.cpp index 6ac278e..0b62857 100644 --- a/generator/vk_layer/source/instance.cpp +++ b/generator/vk_layer/source/instance.cpp @@ -29,6 +29,9 @@ #include "instance.hpp" +/** + * @brief The dispatch lookup for all of the created Vulkan instances. + */ static std::unordered_map> g_instances; /* See header for documentation. */ @@ -42,8 +45,8 @@ void Instance::store( /* See header for documentation. */ Instance* Instance::retrieve( - VkInstance handle) -{ + VkInstance handle +) { void* key = getDispatchKey(handle); assert(isInMap(key, g_instances)); return g_instances.at(key).get(); @@ -51,8 +54,8 @@ Instance* Instance::retrieve( /* See header for documentation. */ Instance* Instance::retrieve( - VkPhysicalDevice handle) -{ + VkPhysicalDevice handle +) { void* key = getDispatchKey(handle); assert(isInMap(key, g_instances)); return g_instances.at(key).get(); @@ -68,7 +71,8 @@ void Instance::destroy( /* See header for documentation. */ Instance::Instance( VkInstance _instance, - PFN_vkGetInstanceProcAddr _nlayerGetProcAddress) : + PFN_vkGetInstanceProcAddr _nlayerGetProcAddress +) : instance(_instance), nlayerGetProcAddress(_nlayerGetProcAddress) { diff --git a/generator/vk_layer/source/instance.hpp b/generator/vk_layer/source/instance.hpp index cfda54e..fc6af6b 100644 --- a/generator/vk_layer/source/instance.hpp +++ b/generator/vk_layer/source/instance.hpp @@ -42,9 +42,8 @@ * Key properties * ============== * - * Unlike EGL contexts, Vulkan instances are designed to be used concurrently - * by multiple application threads. An application can have multiple concurrent - * instances (although this is less common than with OpenGL ES applications), + * Vulkan instances are designed to be used concurrently by multiple + * application threads. An application can have multiple concurrent instances, * and use each instance from multiple threads. * * Access to the layer driver structures must therefore be kept thread-safe. @@ -65,10 +64,6 @@ /** * @brief This class implements the layer state tracker for a single instance. - * - * These objects are relatively light-weight, as they are rarely used once a VkDevice has been - * created, but we need to track the chain-of-ownership as the instance is the root object that - * the application creates when initializing a rendering context. */ class Instance { @@ -87,6 +82,8 @@ class Instance * @brief Fetch an instance from the global store of dispatchable instances. * * @param handle The dispatchable instance handle to use as an indirect lookup. + * + * @return The layer instance context. */ static Instance* retrieve( VkInstance handle); @@ -95,6 +92,8 @@ class Instance * @brief Fetch an instance from the global store of dispatchable instances. * * @param handle The dispatchable physical device handle to use as an indirect lookup. + * + * @return The layer instance context. */ static Instance* retrieve( VkPhysicalDevice handle); diff --git a/generator/vk_layer/source/version.hpp.in b/generator/vk_layer/source/version.hpp.in index 50c30b9..5fcb9c3 100644 --- a/generator/vk_layer/source/version.hpp.in +++ b/generator/vk_layer/source/version.hpp.in @@ -24,9 +24,7 @@ */ /** - * @file - * This header implements placeholder templates that are populated by CMake - * during configure. + * @file Placeholder templates that are populated by CMake during configure. */ #pragma once diff --git a/layer_gpu_timeline/README_LAYER.md b/layer_gpu_timeline/README_LAYER.md index 8f8a56c..a3b0fee 100644 --- a/layer_gpu_timeline/README_LAYER.md +++ b/layer_gpu_timeline/README_LAYER.md @@ -1,163 +1,100 @@ # Layer: GPU Timeline This layer is used with Arm GPUs for tracking submitted schedulable workloads -and emitting semantic information about them. This data can be combined with -the raw workload execution timing information captured using the Android -Perfetto service, providing developers with a richer debug visualization. +and emitting useful metadata that can be used in tooling visualizations. This +data can be combined with raw workload execution timing information captured +by the Android Perfetto service, providing developers with more useful +information about how their application is scheduled on to the Arm GPU. -## What devices? +## What devices are supported? The Arm GPU driver integration with the Perfetto render stages scheduler event trace is supported at production quality since the r47p0 driver version. -However, associating semantics from this layer relies on a further integration -with debug labels which requires an r51p0 or later driver version. +However, associating additional metadata from this layer relies on additional +functionality which requires an r51p0 or later driver version. -## What workloads? +## What workloads are supported? -A schedulable workload is the smallest workload that the Arm GPU command stream -scheduler will issue to the GPU hardware work queues. This includes the -following workload types: +The Arm GPU scheduler event trace can generate timing events for each +atomically schedulable workload submitted to the GPU scheduler. -* Render passes, split into: - * Vertex or Binning phase - * Fragment or Main phase +Most workloads submitted to a Vulkan queue by the application are a single +schedulable entity, for example a compute dispatch or transfer is a single +workload. + +The exception to this is the render pass workload. Arm GPUs are tile-based, so +each group of merged subpasses from a render pass is processed as two +schedulable phases. The first phase - the vertex or binning phase - determines +which primitives contribute to which screen-space tiles. The second phase - the +fragment or main phase - reads the binning information and completes fragment +shading tile-by-tile. + +This layer tracks the following workloads: + +* Render passes * Compute dispatches -* Trace rays +* Trace rays dispatches * Transfers to a buffer * Transfers to an image -Most workloads are dispatched using a single API call, and are trivial to -manage in the layer. However, render passes are more complex and need extra -handling. In particular: - -* Render passes are issued using multiple API calls. -* Useful render pass properties, such as draw count, are not known until the - render pass recording has ended. -* Dynamic render passes using `vkCmdBeginRendering()` and `vkCmdEndRendering()` - can be suspended and resumed across command buffer boundaries. Properties - such as draw count are not defined by the scope of a single command buffer. - ## Tracking workloads -This layer tracks workloads encoded in command buffers, and emits semantic -metadata for each workload via a communications side-channel. A host tool -combines the semantic data stream with the Perfetto data stream, using debug -label tags injected by the layer as a common cross-reference to link across -the streams. - -### Workload labelling - -Command stream labelling is implemented using `vkCmdDebugMarkerBeginEXT()` -and `vkCmdDebugMarkerEndEXT()`, wrapping one layer-owned `tagID` label around -each semantic workload. This `tagID` can unambiguously refer to this workload -encoding, and metadata that we do not expect to change per submit will be -emitted using the matching `tagID` as the sole identifier. - -_**TODO:** Dynamic `submitID` tracking is not yet implemented._ - -The `tagID` label is encoded into the recorded command buffer which means, for -reusable command buffers, it is not an unambiguous identifier of a specific -running workload. To allow us to disambiguate specific workload instances, the -layer can optionally add an outer wrapper of `submitID` labels around each -submitted command buffer. This wrapper is only generated if the submit contains -any command buffers that require the generation of a per-submit annex (see the -following section for when this is needed). - -The `submitID.tagID` pair of IDs uniquely identifies a specific running -workload, and can be used to attach an instance-specific metadata annex to a -specific submitted workload rather than to the shared recorded command buffer. - -### Workload metadata for split render passes - -_**TODO:** Split render pass tracking is not yet implemented._ - -Dynamic render passes can be split across multiple Begin/End pairs, including -being split across command buffer boundaries. If these splits occur within a -single primary command buffer, or its secondaries, it is handled transparently -by the layer and it appears as a single message as if no splits occurred. If -these splits occur across primary command buffer boundaries, then some -additional work is required. - -In our design a `tagID` debug marker is only started when the render pass first -starts (not on resume), and stopped at the end of the render pass (not on -suspend). The same `tagID` is used to refer to all parts of the render pass, -no matter how many times it was suspended and resumed. - -If a render pass splits across command buffers, we cannot precompute metrics -based on `tagID` alone, even if the command buffers are one-time use. This is -because we do not know what combination of submitted command buffers will be -used, and so we cannot know what the render pass contains until submit time. -Split render passes will emit a `submitID.tagID` metadata annex containing -the parameters that can only be known at submit time. - -### Workload metadata for compute dispatches - -_**TODO:** Compute workgroup parsing from the SPIR-V is not yet implemented._ - -Compute workload dispatch is simple to track, but one of the metadata items we -want to export is the total size of the work space (work_group_count * -work_group_size). - -The work group count is defined by the API call, but may be an indirect -parameter (see indirect tracking above). - -The work group size is defined by the program pipeline, and is defined in the -SPIR-V via a literal or a build-time specialization constant. To support this -use case we will need to parse the SPIR-V when the pipeline is built, if -SPIR-V is available. - -### Workload metadata for indirect calls - -_**TODO:** Indirect parameter tracking is not yet implemented._ - -One of the valuable pieces of metadata that we want to present is the size of -each workload. For render passes this is captured at API call time, but for -other workloads the size can be an indirect parameter that is not known when -the triggering API call is made. - -To capture indirect parameters we insert a transfer that copies the indirect -parameters into a layer-owned buffer. To ensure exclusive use of the buffer and -avoid data corruption, each buffer region used is unique to a specific `tagID`. -Attempting to submit the same command buffer multiple times will result in -the workload being serialized to avoid racy access to the buffer. Once the -buffer has been retrieved by the layer, a metadata annex containing the -indirect parameters will be emitted using the `submitID.tagID` pair. This may -be some time later than the original submit. - -### Workload metadata for user-defined labels - -The workload metadata captures user-defined labels that the application -provides using `vkCmdDebugMarkerBeginEXT()` and `vkCmdDebugMarkerEndEXT()`. -These are a stack-based debug mechanism where `Begin` pushes a new entry on to -to the stack, and `End` pops the the most recent level off the stack. - -Workloads are labelled with the stack values that existed when the workload -was started. For render passes this is the value on the stack when, e.g., -`vkCmdBeginRenderPass()` was called. We do not capture any labels that exist -inside the render pass. - -The debug label stack belongs to the queue, not to the command buffer, so the -value of the label stack is not known until submit time. The debug information -for a specific `submitID.tagID` pair is therefore provided as an annex at -submit time once the stack can be resolved. - -## Message protocol - -For each workload in a command buffer, or part-workload in the case of a -suspended render pass, we record a JSON metadata blob containing the payload -we want to send. - -The low level protocol message contains: - -* Message type `uint8_t` -* Sequence ID `uint64_t` (optional, implied by message type) -* Tag ID `uint64_t` -* JSON length `uint32_t` -* JSON payload `uint8_t[]` - -Each workload will read whatever properties it can from the `tagID` metadata -and will then merge in all fields from any subsequent `sequenceID.tagID` -metadata that matches. +The latest Arm driver integration with the Perfetto profiler propagates +application debug labels into the GPU Render Stages scheduler events. The debug +labels are the label stack created using either of these Vulkan methods: + +* `vkCmdBegin/EndDebugUtilsLabelEXT()` +* `vkCmdDebugMarkerBegin/EndEXT()` + +This layer utilizes this mechanism to wrap each submitted workload in a command +buffer with a unique `tagID` which identifies that recorded workload. A +metadata side-channel provides the metadata for each workload, annotating each +metadata record with the matching `tagID` to allow them to be cross-referenced +later. + +### Limitation: Indirect dispatches and trace rays + +The current implementation captures the metadata parameters when the command +buffer is recorded. The layer does not currently support asynchronous capture +of indirect parameter buffers. Indirect dispatch and trace rays are still +captured and reported, but with unknown workload dimensions. + +### Limitation: Compute dispatch sizes + +The current implementation reports the size of a compute workload as the +number of work groups, because this is the parameter used by the API. We +eventually want to report this as the number of work items, but the parsing +of the SPIR-V and pipeline parameters has not yet been implemented. + +### Limitation: Dynamic render passes split over multiple command buffers + +The label containing the `tagID` is recorded into the application command +buffer when the command buffer is recorded. The workload-to-metadata mapping +requires that every use of a `tagID` has the same properties, or we will +be unable to associate the correct metadata with its matching workload. + +Content that splits a render pass over multiple command buffers that +are not one-time-submit violates this requirement. Multiple submits of a render +pass with a single `tagID` may have different numbers of draw calls, depending +on the number of draws that occur in the later command buffers that resume the +render pass. When the layer detects suspended render pass in a multi-submit +command buffer, it will still capture and report the workload, but with an +unknown draw call count. + +## Command stream modelling + +Most properties we track are a property of the command buffer recording in +isolation. However, the user debug label stack is a property of the queue and +persists across submits. We can therefore only determine the debug label +associated with a workload in the command stream at submit time, and must +resolve it per workload inside the command buffer. + +To support this we implement a software command stream that contains simple +bytecode actions that represent the sequence of debug label and workload +commands inside each command buffer. This "command stream" can be played to +update the the queue state at submit time, triggering metadata submission +for each workload that can snapshot the current state of the user debug label +stack at that point in the command stream. - - - diff --git a/layer_gpu_timeline/android_build.sh b/layer_gpu_timeline/android_build.sh index 5bdbff5..960b2b0 100644 --- a/layer_gpu_timeline/android_build.sh +++ b/layer_gpu_timeline/android_build.sh @@ -67,7 +67,7 @@ cmake \ -DCMAKE_TOOLCHAIN_FILE="${ANDROID_NDK_HOME}/build/cmake/android.toolchain.cmake" \ .. -make -j8 +make -j1 popd diff --git a/layer_gpu_timeline/docs/command_buffer_model.md b/layer_gpu_timeline/docs/command_buffer_model.md deleted file mode 100644 index f317b51..0000000 --- a/layer_gpu_timeline/docs/command_buffer_model.md +++ /dev/null @@ -1,155 +0,0 @@ -# Layer: GPU Timeline - Command Buffer Modelling - -One of the main challenges of this layer driver is modelling behavior in queues -and command buffers that is not known until submit time, and then taking -appropriate actions based on the combination of both the head state of the -queue and the content of the pre-recorded command buffers. - -Our design to solve this is a lightweight software command stream which is -recorded when a command buffer is recorded, and then executed when the -command buffer is submitted to the queue. Just like a real hardware command -stream these commands can update state or trigger some other action we need -performed. - -## Layer commands - -**MARKER_BEGIN(const std::string\*):** - -* Push a new marker into the queue debug label stack. - -**MARKER_END():** - -* Pop the latest marker from the queue debug label stack. - -**RENDER_PASS(const json\*):** - -* Set the current workload to a new render pass with the passed metadata. - -**RENDERPASS_RESUME(const json\*):** - -* Update the current workload, which must be a render pass, with extra - draw count metadata. - -**COMPUTE_DISPATCH_BEGIN(const json\*):** - -* Set the current workload to a new compute dispatch with the passed metadata. - -**TRACE_RAYS_BEGIN(const json\*):** - -* Set the current workload to a new trace rays with the passed metadata. - -**BUFFER_TRANSFER_BEGIN(const json\*):** - -* Set the current workload to a new a buffer transfer. - -**IMAGE_TRANSFER(const json\*):** - -* Set the current workload to a new image transfer. - -**WORKLOAD_END():** - -* Mark the current workload as complete, and emit a built metadata entry for - it. - -## Layer command recording - -Command buffer recording is effectively building two separate state -structures for the layer. - -The first is a per-workload or per-restart JSON structure that contains the -metadata we need for that workload. For partial workloads - e.g. a dynamic -render pass begin that has been suspended - this metadata will be partial and -rely on later restart metadata to complete it. - -The second is the layer "command stream" that contains the bytecode commands -to execute when the command buffer is submitted to the queue. These commands -are very simple, consisting of a list of command+pointer pairs, where the -pointer value may be unused by some commands. Commands are stored in a -std::vector, but we reserve enough memory to store 256 commands without -reallocating which is enough for the majority of command buffers we see in -real applications. - -The command stream for a secondary command buffer is inlined into the primary -command buffer during recording. - -### Recording sequence - -When application records a new workload: - - * A `tagID` is assigned and recorded using `vkCmdMarkerBegin()` label in the - Vulkan command stream _before_ the new workload is written to the command - stream. - * If workload is using indirect parameters, then a transfer job to copy - indirect parameters into a layer-owned buffer is emitted _before_ the new - workload. No additional barrier is needed because application barriers must - have already ensured that the indirect parameter buffer is valid. - * A proxy workload object is created in the layer storing the assigned - `tagID` and all settings that are known at command recording time. - * A layer command stream command is recorded into the submit time stream - indicating `_BEGIN` with a pointer to the proxy workload. Note that - this JSON may be modified later for some workloads. - * If workload is using indirect parameters, a layer command stream command is - recorded into the resolve time stream, which will handle cleanup and - emitting the `submitID.tagID` annex message for the indirect data. - * If the command buffer is not ONE_TIME_SUBMIT, if any workload is using - indirect parameters, or contains incomplete render passes, the command - buffer is marked as needing a `submitID` wrapper. - * The user command is written to the Vulkan command stream. - -When application resumes a render pass workload: - - * A `tagID` of zero is assigned, but not emitted to the command stream. - * A layer command stream command is recorded into the submit time stream - indicating `_RESUME` with a pointer to the proxy workload. Note that - this JSON may be modified later for some workloads. - * The user command is written to the Vulkan command stream. - -When application ends a workload: - - * For render pass workloads, any statistics accumulated since the last begin - are rolled up into the proxy workload object. - * For render pass workloads, the user command is written to the Vulkan - command stream. - * The command steam label scope is closed using `vkCmdMarkerEnd()`. - -## Layer command playback - -The persistent state for command playback belongs to the queues the command -buffers are submitted to. The command stream bytecode is run by a bytecode -interpreter associated with the state of the current queue, giving the -interpreter access to the current `submitID` and queue debug label stack. - -### Submitting sequence - -For each command buffer in the user submit: - -* If the command buffer needs a `submitID` we allocate a unique `submitID` and - create two new command buffers that will wrap the user command buffer with an - additional stack layer of debug label containing the `s` string. We will - inject a layer command stream async command to handle freeing the command - buffers. -* The tool will process the submit-time layer commands, executing each command - to either update some state or emit -* If there are any async layer commands, either recorded in the command buffer - or from the wrapping command buffers, we will need to add an async handler. - This cannot safely use the user fence or depend on any user object lifetime, - so we will add a layer-owned timeline semaphore to the submit which we can - wait on to determine when it is safe trigger the async work. - -## Future: Async commands - -One of our longer-term goals is to be able to capture indirect parameters, -which will be available after-the-fact once the GPU has processed the command -buffer. Once we have the data we can emit an annex message containing -parameters for each indirect `submitID.tagID` pair in the command buffer. - -We need to be able to emit the metadata after the commands are complete, -and correctly synchronize use of the indirect capture staging buffer -if command buffers are reissued. My current thinking is that we would -implement this using additional layer commands that are processed on submit, -including support for async commands that run in a separate thread and -wait on the command buffer completion fence before running. - -- - - - -_Copyright © 2024, Arm Limited and contributors._ diff --git a/layer_gpu_timeline/source/device.cpp b/layer_gpu_timeline/source/device.cpp index 271aab3..1e140ff 100644 --- a/layer_gpu_timeline/source/device.cpp +++ b/layer_gpu_timeline/source/device.cpp @@ -35,6 +35,9 @@ #include "device.hpp" #include "instance.hpp" +/** + * @brief The dispatch lookup for all of the created Vulkan devices. + */ static std::unordered_map> g_devices; /* See header for documentation. */ @@ -54,8 +57,8 @@ void Device::store( /* See header for documentation. */ Device* Device::retrieve( - VkDevice handle) -{ + VkDevice handle +) { void* key = getDispatchKey(handle); assert(isInMap(key, g_devices)); return g_devices.at(key).get(); @@ -63,8 +66,8 @@ Device* Device::retrieve( /* See header for documentation. */ Device* Device::retrieve( - VkQueue handle) -{ + VkQueue handle +) { void* key = getDispatchKey(handle); assert(isInMap(key, g_devices)); return g_devices.at(key).get(); @@ -72,8 +75,8 @@ Device* Device::retrieve( /* See header for documentation. */ Device* Device::retrieve( - VkCommandBuffer handle) -{ + VkCommandBuffer handle +) { void* key = getDispatchKey(handle); assert(isInMap(key, g_devices)); return g_devices.at(key).get(); @@ -92,7 +95,8 @@ Device::Device( VkPhysicalDevice _physicalDevice, VkDevice _device, PFN_vkGetDeviceProcAddr nlayerGetProcAddress -): instance(_instance), +): + instance(_instance), physicalDevice(_physicalDevice), device(_device) { @@ -105,9 +109,3 @@ Device::Device( commsWrapper = std::make_unique(*commsModule); } } - -/* See header for documentation. */ -Device::~Device() -{ - -} diff --git a/layer_gpu_timeline/source/device.hpp b/layer_gpu_timeline/source/device.hpp index 2d5460d..b56de83 100644 --- a/layer_gpu_timeline/source/device.hpp +++ b/layer_gpu_timeline/source/device.hpp @@ -24,8 +24,7 @@ */ /** - * @file - * Declares the root class for layer management of VkDevice objects. + * @file Declares the root class for layer management of VkDevice objects. * * Role summary * ============ @@ -41,10 +40,9 @@ * Key properties * ============== * - * Unlike EGL contexts, Vulkan devices are designed to be used concurrently by - * multiple application threads. An application can have multiple concurrent - * devices (although this is less common than with OpenGL ES applications), and - * use each device from multiple threads. + * Vulkan devices are designed to be used concurrently by multiple application + * threads. An application can have multiple concurrent devices, and use each + * device from multiple threads. * * Access to the layer driver structures must therefore be kept thread-safe. * For sake of simplicity, we generally implement this by: @@ -83,6 +81,8 @@ class Device * @brief Fetch a device from the global store of dispatchable devices. * * @param handle The dispatchable device handle to use as an indirect lookup. + * + * @return The layer device context. */ static Device* retrieve( VkDevice handle); @@ -91,6 +91,8 @@ class Device * @brief Fetch a device from the global store of dispatchable devices. * * @param handle The dispatchable queue handle to use as an indirect lookup. + * + * @return The layer device context. */ static Device* retrieve( VkQueue handle); @@ -99,6 +101,8 @@ class Device * @brief Fetch a device from the global store of dispatchable devices. * * @param handle The dispatchable command buffer handle to use as an indirect lookup. + * + * @return The layer device context. */ static Device* retrieve( VkCommandBuffer handle); @@ -117,7 +121,7 @@ class Device * @param instance The layer instance object this device is created with. * @param physicalDevice The physical device this logical device is for. * @param device The device handle this device is created with. - * @param nlayerGetProcAddress The vkGetProcAddress function in the driver/next layer down. + * @param nlayerGetProcAddress The vkGetDeviceProcAddress function for the driver. */ Device( Instance* instance, @@ -128,21 +132,27 @@ class Device /** * @brief Destroy this layer device object. */ - ~Device(); + ~Device() = default; /** - * @brief Callback for sending messages + * @brief Callback for sending messages on frame boundary. + * + * @param message The message to send. */ - void onFrame(const std::string& message) - { + void onFrame( + const std::string& message + ) { commsWrapper->txMessage(message); } /** - * @brief Callback for sending messages + * @brief Callback for sending messages on workload submit to a queue. + * + * @param message The message to send. */ - void onWorkloadSubmit(const std::string& message) - { + void onWorkloadSubmit( + const std::string& message + ) { commsWrapper->txMessage(message); } @@ -177,17 +187,17 @@ class Device const VkDevice device; /** - * @brief State tracking for this device; + * @brief State tracker for this device. */ Tracker::Device stateTracker; /** - * @brief Communications module. + * @brief Shared network communications module. */ static std::unique_ptr commsModule; /** - * @brief Communications module message encoder. + * @brief Shared network communications message encoder. */ static std::unique_ptr commsWrapper; }; diff --git a/layer_gpu_timeline/source/device_utils.hpp b/layer_gpu_timeline/source/device_utils.hpp new file mode 100644 index 0000000..eddf193 --- /dev/null +++ b/layer_gpu_timeline/source/device_utils.hpp @@ -0,0 +1,56 @@ +/* + * SPDX-License-Identifier: MIT + * ---------------------------------------------------------------------------- + * Copyright (c) 2024 Arm Limited + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * ---------------------------------------------------------------------------- + */ + +#pragma once + +#include + +#include "framework/utils.hpp" + +#include "device.hpp" + +/** + * @brief Emit a start tag via a driver debug utils label. + * + * @param layer The layer context for the device. + * @param commandBuffer The command buffer we are recording. + * @param tagID The tagID to emit into the label. + */ +[[maybe_unused]] static void emitStartTag( + Device* layer, + VkCommandBuffer commandBuffer, + uint64_t tagID +) { + // Emit the unique workload tag into the command stream + std::string tagLabel = formatString("t%" PRIu64, tagID); + VkDebugUtilsLabelEXT tagInfo { + .sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_LABEL_EXT, + .pNext = nullptr, + .pLabelName = tagLabel.c_str(), + .color = { 0.0f, 0.0f, 0.0f, 0.0f } + }; + + layer->driver.vkCmdBeginDebugUtilsLabelEXT(commandBuffer, &tagInfo); +} diff --git a/layer_gpu_timeline/source/instance.cpp b/layer_gpu_timeline/source/instance.cpp index 6ac278e..0b62857 100644 --- a/layer_gpu_timeline/source/instance.cpp +++ b/layer_gpu_timeline/source/instance.cpp @@ -29,6 +29,9 @@ #include "instance.hpp" +/** + * @brief The dispatch lookup for all of the created Vulkan instances. + */ static std::unordered_map> g_instances; /* See header for documentation. */ @@ -42,8 +45,8 @@ void Instance::store( /* See header for documentation. */ Instance* Instance::retrieve( - VkInstance handle) -{ + VkInstance handle +) { void* key = getDispatchKey(handle); assert(isInMap(key, g_instances)); return g_instances.at(key).get(); @@ -51,8 +54,8 @@ Instance* Instance::retrieve( /* See header for documentation. */ Instance* Instance::retrieve( - VkPhysicalDevice handle) -{ + VkPhysicalDevice handle +) { void* key = getDispatchKey(handle); assert(isInMap(key, g_instances)); return g_instances.at(key).get(); @@ -68,7 +71,8 @@ void Instance::destroy( /* See header for documentation. */ Instance::Instance( VkInstance _instance, - PFN_vkGetInstanceProcAddr _nlayerGetProcAddress) : + PFN_vkGetInstanceProcAddr _nlayerGetProcAddress +) : instance(_instance), nlayerGetProcAddress(_nlayerGetProcAddress) { diff --git a/layer_gpu_timeline/source/instance.hpp b/layer_gpu_timeline/source/instance.hpp index cfda54e..fc6af6b 100644 --- a/layer_gpu_timeline/source/instance.hpp +++ b/layer_gpu_timeline/source/instance.hpp @@ -42,9 +42,8 @@ * Key properties * ============== * - * Unlike EGL contexts, Vulkan instances are designed to be used concurrently - * by multiple application threads. An application can have multiple concurrent - * instances (although this is less common than with OpenGL ES applications), + * Vulkan instances are designed to be used concurrently by multiple + * application threads. An application can have multiple concurrent instances, * and use each instance from multiple threads. * * Access to the layer driver structures must therefore be kept thread-safe. @@ -65,10 +64,6 @@ /** * @brief This class implements the layer state tracker for a single instance. - * - * These objects are relatively light-weight, as they are rarely used once a VkDevice has been - * created, but we need to track the chain-of-ownership as the instance is the root object that - * the application creates when initializing a rendering context. */ class Instance { @@ -87,6 +82,8 @@ class Instance * @brief Fetch an instance from the global store of dispatchable instances. * * @param handle The dispatchable instance handle to use as an indirect lookup. + * + * @return The layer instance context. */ static Instance* retrieve( VkInstance handle); @@ -95,6 +92,8 @@ class Instance * @brief Fetch an instance from the global store of dispatchable instances. * * @param handle The dispatchable physical device handle to use as an indirect lookup. + * + * @return The layer instance context. */ static Instance* retrieve( VkPhysicalDevice handle); diff --git a/layer_gpu_timeline/source/layer_device_functions.hpp b/layer_gpu_timeline/source/layer_device_functions.hpp index 3806398..8c2f8b5 100644 --- a/layer_gpu_timeline/source/layer_device_functions.hpp +++ b/layer_gpu_timeline/source/layer_device_functions.hpp @@ -23,6 +23,8 @@ * ---------------------------------------------------------------------------- */ +#pragma once + #include #include "framework/utils.hpp" diff --git a/layer_gpu_timeline/source/layer_device_functions_command_buffer.cpp b/layer_gpu_timeline/source/layer_device_functions_command_buffer.cpp index f0cc338..ef8e920 100644 --- a/layer_gpu_timeline/source/layer_device_functions_command_buffer.cpp +++ b/layer_gpu_timeline/source/layer_device_functions_command_buffer.cpp @@ -23,9 +23,7 @@ * ---------------------------------------------------------------------------- */ -#include #include -#include #include "device.hpp" #include "layer_device_functions.hpp" diff --git a/layer_gpu_timeline/source/layer_device_functions_command_pool.cpp b/layer_gpu_timeline/source/layer_device_functions_command_pool.cpp index 31bc1b4..a640a90 100644 --- a/layer_gpu_timeline/source/layer_device_functions_command_pool.cpp +++ b/layer_gpu_timeline/source/layer_device_functions_command_pool.cpp @@ -23,9 +23,7 @@ * ---------------------------------------------------------------------------- */ -#include #include -#include #include "device.hpp" #include "layer_device_functions.hpp" diff --git a/layer_gpu_timeline/source/layer_device_functions_debug.cpp b/layer_gpu_timeline/source/layer_device_functions_debug.cpp index 4c1e1d9..1905193 100644 --- a/layer_gpu_timeline/source/layer_device_functions_debug.cpp +++ b/layer_gpu_timeline/source/layer_device_functions_debug.cpp @@ -23,9 +23,7 @@ * ---------------------------------------------------------------------------- */ -#include #include -#include #include "device.hpp" #include "layer_device_functions.hpp" diff --git a/layer_gpu_timeline/source/layer_device_functions_dispatch.cpp b/layer_gpu_timeline/source/layer_device_functions_dispatch.cpp index 7555501..de5ee10 100644 --- a/layer_gpu_timeline/source/layer_device_functions_dispatch.cpp +++ b/layer_gpu_timeline/source/layer_device_functions_dispatch.cpp @@ -23,15 +23,25 @@ * ---------------------------------------------------------------------------- */ -#include #include -#include #include "device.hpp" +#include "device_utils.hpp" #include "layer_device_functions.hpp" extern std::mutex g_vulkanLock; +/** + * @brief Register a compute dispatch with the tracker. + * + * @param layer The layer context for the device. + * @param commandBuffer The command buffer we are recording. + * @param groupX The X size of the dispatch in groups. + * @param groupY The Y size of the dispatch in groups. + * @param groupZ The Z size of the dispatch in groups. + * + * @return The assigned tagID for the workload. + */ static uint64_t registerDispatch( Device* layer, VkCommandBuffer commandBuffer, @@ -44,23 +54,6 @@ static uint64_t registerDispatch( return cb.dispatch(groupX, groupY, groupZ); } -static void emitStartTag( - Device* layer, - VkCommandBuffer commandBuffer, - uint64_t tagID -) { - // Emit the unique workload tag into the command stream - std::string tagLabel = formatString("t%" PRIu64, tagID); - VkDebugUtilsLabelEXT tagInfo { - .sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_LABEL_EXT, - .pNext = nullptr, - .pLabelName = tagLabel.c_str(), - .color = { 0.0f, 0.0f, 0.0f, 0.0f } - }; - - layer->driver.vkCmdBeginDebugUtilsLabelEXT(commandBuffer, &tagInfo); -} - /* See Vulkan API for documentation. */ template <> VKAPI_ATTR void VKAPI_CALL layer_vkCmdDispatch( diff --git a/layer_gpu_timeline/source/layer_device_functions_draw_call.cpp b/layer_gpu_timeline/source/layer_device_functions_draw_call.cpp index 49cf669..42350d0 100644 --- a/layer_gpu_timeline/source/layer_device_functions_draw_call.cpp +++ b/layer_gpu_timeline/source/layer_device_functions_draw_call.cpp @@ -32,6 +32,12 @@ extern std::mutex g_vulkanLock; +/** + * @brief Register a draw call with the tracker. + * + * @param layer The layer context for the device. + * @param commandBuffer The command buffer we are recording. + */ static void registerDrawCall( Device* layer, VkCommandBuffer commandBuffer diff --git a/layer_gpu_timeline/source/layer_device_functions_queue.cpp b/layer_gpu_timeline/source/layer_device_functions_queue.cpp index 30ca611..a5c92e2 100644 --- a/layer_gpu_timeline/source/layer_device_functions_queue.cpp +++ b/layer_gpu_timeline/source/layer_device_functions_queue.cpp @@ -23,12 +23,10 @@ * ---------------------------------------------------------------------------- */ -#include #include -#include +#include #include "utils/misc.hpp" -#include "nlohmann/json.hpp" #include "device.hpp" #include "layer_device_functions.hpp" diff --git a/layer_gpu_timeline/source/layer_device_functions_render_pass.cpp b/layer_gpu_timeline/source/layer_device_functions_render_pass.cpp index b59f305..5d16880 100644 --- a/layer_gpu_timeline/source/layer_device_functions_render_pass.cpp +++ b/layer_gpu_timeline/source/layer_device_functions_render_pass.cpp @@ -23,16 +23,15 @@ * ---------------------------------------------------------------------------- */ -#include #include -#include - -#include "device.hpp" -#include "layer_device_functions.hpp" #include "framework/utils.hpp" #include "trackers/render_pass.hpp" +#include "device.hpp" +#include "device_utils.hpp" +#include "layer_device_functions.hpp" + extern std::mutex g_vulkanLock; /* See Vulkan API for documentation. */ @@ -166,18 +165,9 @@ VKAPI_ATTR void VKAPI_CALL layer_vkCmdBeginRenderPass( // Notify the command buffer we are starting a new render pass uint64_t tagID = cb.renderPassBegin(rp, width, height); - // Emit the unique workload tag into the command stream - std::string tagLabel = formatString("t%" PRIu64, tagID); - VkDebugUtilsLabelEXT tagInfo { - .sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_LABEL_EXT, - .pNext = nullptr, - .pLabelName = tagLabel.c_str(), - .color = { 0.0f, 0.0f, 0.0f, 0.0f } - }; - // Release the lock to call into the driver lock.unlock(); - layer->driver.vkCmdBeginDebugUtilsLabelEXT(commandBuffer, &tagInfo); + emitStartTag(layer, commandBuffer, tagID); layer->driver.vkCmdBeginRenderPass(commandBuffer, pRenderPassBegin, contents); } @@ -204,18 +194,9 @@ VKAPI_ATTR void VKAPI_CALL layer_vkCmdBeginRenderPass2( // Notify the command buffer we are starting a new render pass uint64_t tagID = cb.renderPassBegin(rp, width, height); - // Emit the unique workload tag into the command stream - std::string tagLabel = formatString("t%" PRIu64, tagID); - VkDebugUtilsLabelEXT tagInfo { - .sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_LABEL_EXT, - .pNext = nullptr, - .pLabelName = tagLabel.c_str(), - .color = { 0.0f, 0.0f, 0.0f, 0.0f } - }; - // Release the lock to call into the driver lock.unlock(); - layer->driver.vkCmdBeginDebugUtilsLabelEXT(commandBuffer, &tagInfo); + emitStartTag(layer, commandBuffer, tagID); layer->driver.vkCmdBeginRenderPass2(commandBuffer, pRenderPassBegin, pSubpassBeginInfo); } @@ -242,18 +223,9 @@ VKAPI_ATTR void VKAPI_CALL layer_vkCmdBeginRenderPass2KHR( // Notify the command buffer we are starting a new render pass uint64_t tagID = cb.renderPassBegin(rp, width, height); - // Emit the unique workload tag into the command stream - std::string tagLabel = formatString("t%" PRIu64, tagID); - VkDebugUtilsLabelEXT tagInfo { - .sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_LABEL_EXT, - .pNext = nullptr, - .pLabelName = tagLabel.c_str(), - .color = { 0.0f, 0.0f, 0.0f, 0.0f } - }; - // Release the lock to call into the driver lock.unlock(); - layer->driver.vkCmdBeginDebugUtilsLabelEXT(commandBuffer, &tagInfo); + emitStartTag(layer, commandBuffer, tagID); layer->driver.vkCmdBeginRenderPass2KHR(commandBuffer, pRenderPassBegin, pSubpassBeginInfo); } @@ -286,22 +258,11 @@ VKAPI_ATTR void VKAPI_CALL layer_vkCmdBeginRendering( // Release the lock to call into the driver lock.unlock(); - // Emit the label only for new render passes if (!resuming) { - // Emit the unique workload tag into the command stream - std::string tagLabel = formatString("t%" PRIu64, tagID); - VkDebugUtilsLabelEXT tagInfo { - .sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_LABEL_EXT, - .pNext = nullptr, - .pLabelName = tagLabel.c_str(), - .color = { 0.0f, 0.0f, 0.0f, 0.0f } - }; - - layer->driver.vkCmdBeginDebugUtilsLabelEXT(commandBuffer, &tagInfo); + emitStartTag(layer, commandBuffer, tagID); } - layer->driver.vkCmdBeginRendering(commandBuffer, pRenderingInfo); } @@ -334,22 +295,11 @@ VKAPI_ATTR void VKAPI_CALL layer_vkCmdBeginRenderingKHR( // Release the lock to call into the driver lock.unlock(); - // Emit the label only for new render passes if (!resuming) { - // Emit the unique workload tag into the command stream - std::string tagLabel = formatString("t%" PRIu64, tagID); - VkDebugUtilsLabelEXT tagInfo { - .sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_LABEL_EXT, - .pNext = nullptr, - .pLabelName = tagLabel.c_str(), - .color = { 0.0f, 0.0f, 0.0f, 0.0f } - }; - - layer->driver.vkCmdBeginDebugUtilsLabelEXT(commandBuffer, &tagInfo); + emitStartTag(layer, commandBuffer, tagID); } - layer->driver.vkCmdBeginRenderingKHR(commandBuffer, pRenderingInfo); } diff --git a/layer_gpu_timeline/source/layer_device_functions_trace_rays.cpp b/layer_gpu_timeline/source/layer_device_functions_trace_rays.cpp index 2d99a3b..d453a29 100644 --- a/layer_gpu_timeline/source/layer_device_functions_trace_rays.cpp +++ b/layer_gpu_timeline/source/layer_device_functions_trace_rays.cpp @@ -28,10 +28,22 @@ #include #include "device.hpp" +#include "device_utils.hpp" #include "layer_device_functions.hpp" extern std::mutex g_vulkanLock; +/** + * @brief Register a trace rays dispatch with the tracker. + * + * @param layer The layer context for the device. + * @param commandBuffer The command buffer we are recording. + * @param itemsX The X size of the dispatch in work items. + * @param itemsY The Y size of the dispatch in work items. + * @param itemsZ The Z size of the dispatch in work items. + * + * @return The assigned tagID for the workload. + */ static uint64_t registerTraceRays( Device* layer, VkCommandBuffer commandBuffer, @@ -44,23 +56,6 @@ static uint64_t registerTraceRays( return cb.traceRays(itemsX, itemsY, itemsZ); } -static void emitStartTag( - Device* layer, - VkCommandBuffer commandBuffer, - uint64_t tagID -) { - // Emit the unique workload tag into the command stream - std::string tagLabel = formatString("t%" PRIu64, tagID); - VkDebugUtilsLabelEXT tagInfo { - .sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_LABEL_EXT, - .pNext = nullptr, - .pLabelName = tagLabel.c_str(), - .color = { 0.0f, 0.0f, 0.0f, 0.0f } - }; - - layer->driver.vkCmdBeginDebugUtilsLabelEXT(commandBuffer, &tagInfo); -} - /* See Vulkan API for documentation. */ template <> VKAPI_ATTR void VKAPI_CALL layer_vkCmdTraceRaysIndirect2KHR( diff --git a/layer_gpu_timeline/source/layer_device_functions_transfer.cpp b/layer_gpu_timeline/source/layer_device_functions_transfer.cpp index 066c23f..3aca1a7 100644 --- a/layer_gpu_timeline/source/layer_device_functions_transfer.cpp +++ b/layer_gpu_timeline/source/layer_device_functions_transfer.cpp @@ -28,10 +28,21 @@ #include #include "device.hpp" +#include "device_utils.hpp" #include "layer_device_functions.hpp" extern std::mutex g_vulkanLock; +/** + * @brief Register a transfer to a buffer with the tracker. + * + * @param layer The layer context for the device. + * @param commandBuffer The command buffer we are recording. + * @param transferType The type of transfer being performed. + * @param byteCount The number of bytes transferred. + * + * @return The assigned tagID for the workload. + */ static uint64_t registerBufferTransfer( Device* layer, VkCommandBuffer commandBuffer, @@ -43,6 +54,16 @@ static uint64_t registerBufferTransfer( return cb.bufferTransfer(transferType, byteCount); } +/** + * @brief Register a transfer to an image with the tracker. + * + * @param layer The layer context for the device. + * @param commandBuffer The command buffer we are recording. + * @param transferType The type of transfer being performed. + * @param pixelCount The number of pixels transferred. + * + * @return The assigned tagID for the workload. + */ static uint64_t registerImageTransfer( Device* layer, VkCommandBuffer commandBuffer, @@ -54,23 +75,6 @@ static uint64_t registerImageTransfer( return cb.imageTransfer(transferType, pixelCount); } -static void emitStartTag( - Device* layer, - VkCommandBuffer commandBuffer, - uint64_t tagID -) { - // Emit the unique workload tag into the command stream - std::string tagLabel = formatString("t%" PRIu64, tagID); - VkDebugUtilsLabelEXT tagInfo { - .sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_LABEL_EXT, - .pNext = nullptr, - .pLabelName = tagLabel.c_str(), - .color = { 0.0f, 0.0f, 0.0f, 0.0f } - }; - - layer->driver.vkCmdBeginDebugUtilsLabelEXT(commandBuffer, &tagInfo); -} - // Commands for transfers /* See Vulkan API for documentation. */ diff --git a/layer_gpu_timeline/source/timeline_comms.cpp b/layer_gpu_timeline/source/timeline_comms.cpp index fbb496a..125acef 100644 --- a/layer_gpu_timeline/source/timeline_comms.cpp +++ b/layer_gpu_timeline/source/timeline_comms.cpp @@ -27,16 +27,19 @@ #include "timeline_comms.hpp" +/* See header for documentation. */ TimelineComms::TimelineComms( - Comms::CommsInterface& commsIf) + Comms::CommsInterface& _comms +): + comms(_comms) { - comms = &commsIf; - if (comms->isConnected()) + if (comms.isConnected()) { - endpoint = comms->getEndpointID("GPUTimeline"); + endpoint = comms.getEndpointID("GPUTimeline"); } } +/* See header for documentation. */ void TimelineComms::txMessage( const std::string& message) { @@ -47,5 +50,5 @@ void TimelineComms::txMessage( } auto data = std::make_unique(message.begin(), message.end()); - comms->txAsync(endpoint, std::move(data)); + comms.txAsync(endpoint, std::move(data)); } diff --git a/layer_gpu_timeline/source/timeline_comms.hpp b/layer_gpu_timeline/source/timeline_comms.hpp index 435f37a..84a3c8a 100644 --- a/layer_gpu_timeline/source/timeline_comms.hpp +++ b/layer_gpu_timeline/source/timeline_comms.hpp @@ -23,20 +23,49 @@ * ---------------------------------------------------------------------------- */ +/** + * @file Declares a simple comms encoded for the timeline layer. + */ + #pragma once #include "comms/comms_interface.hpp" +/** + * @brief A simple message encoder for the timeline comms endpoint. + * + * TODO: This is currently a very simple implementation because we are simply + * passing JSON strings around. This is not the most efficient way of doing + * this and in future this module will be used to implement binary encoders + * for each specific message type that needs sending. + */ class TimelineComms { public: + /** + * @brief Construct a new encoder. + * + * @param comms The common comms module used by all services. + */ TimelineComms( Comms::CommsInterface& comms); + /** + * @brief Send a message to the GPU timeline endpoint service. + * + * @param message The message to send. + */ void txMessage( const std::string& message); private: + /** + * @brief The endpoint ID of the service, or 0 if not found. + */ Comms::EndpointID endpoint { 0 }; - Comms::CommsInterface* comms { nullptr }; + + /** + * @brief The common module for network messaging. + */ + Comms::CommsInterface& comms; }; diff --git a/layer_gpu_timeline/source/version.hpp.in b/layer_gpu_timeline/source/version.hpp.in index 50c30b9..5fcb9c3 100644 --- a/layer_gpu_timeline/source/version.hpp.in +++ b/layer_gpu_timeline/source/version.hpp.in @@ -24,9 +24,7 @@ */ /** - * @file - * This header implements placeholder templates that are populated by CMake - * during configure. + * @file Placeholder templates that are populated by CMake during configure. */ #pragma once