GCC Code Coverage Report


ernie/
Coverage:
low: ≥ 0%
medium: ≥ 75.0%
high: ≥ 90.0%
Lines:
7 / 7
100.0%
Branches:
5 / 6
83.3%
Functions:
1 / 1, 2 excl
100.0%

graph/dependencies.h
Line Branch Exec Source
1 // SPDX-License-Identifier: BSD-3-Clause
2 /// @file dependencies.h
3 /// @brief The frame graph's dependency analysis: the synchronization2 barriers between passes in
4 /// their order, computed on the host and tested without a device.
5 ///
6 /// Images stay in the general layout, which the bindless heap's descriptors name, except while a
7 /// raster pass uses them as attachments. Buffers are ranges of arenas reached by address, so
8 /// their dependencies are recorded as memory barriers.
9 ///
10 /// References:
11 /// - H.-K. Arntzen, "Render graphs and Vulkan — a deep dive", 2017.
12 /// https://themaister.net/blog/2017/08/15/render-graphs-and-vulkan-a-deep-dive/
13 /// - Khronos, "Synchronization Examples".
14 /// https://github.com/KhronosGroup/Vulkan-Docs/wiki/Synchronization-Examples
15 #pragma once
16
17 #include "ernie/device/vulkan.h"
18
19 #include <cstdint>
20 #include <span>
21 #include <string>
22 #include <vector>
23
24 namespace ernie::graph {
25
26 /// @brief How a pass uses a resource; with the pass's `Kind`, it decides stages, accesses and
27 /// image layouts.
28 enum class Use : std::uint8_t {
29 /// Sampled through a texture descriptor.
30 Sampled,
31 /// Read as a storage image or buffer.
32 StorageRead,
33 /// Written as a storage image or buffer.
34 StorageWrite,
35 /// Read and written as a storage image or buffer.
36 StorageReadWrite,
37 /// A colour attachment, cleared or overwritten.
38 ColorWrite,
39 /// A colour attachment, loaded or blended.
40 ColorReadWrite,
41 /// A depth attachment, cleared or overwritten.
42 DepthWrite,
43 /// A depth attachment, loaded and written.
44 DepthReadWrite,
45 /// A depth attachment, tested only.
46 DepthRead,
47 /// The source of a copy or blit.
48 CopySrc,
49 /// The destination of a copy, blit or fill.
50 CopyDst,
51 /// A uniform buffer.
52 Uniform,
53 /// Indirect draw or dispatch arguments.
54 Indirect,
55 /// Read by the host once the graph's ticket is reached.
56 HostRead,
57 /// Read by an acceleration structure build: geometry, instances, or the structures a
58 /// top-level one refers to.
59 BuildInput,
60 /// A build's scratch memory, read and written.
61 BuildScratch,
62 /// The acceleration structure a build writes.
63 BuildOutput,
64 /// An acceleration structure that ray queries or ray tracing shaders trace.
65 Trace,
66 };
67
68 /// @brief What a pass records, deciding its stages.
69 enum class Kind : std::uint8_t {
70 /// Dispatches.
71 Compute,
72 /// Draws within dynamic rendering.
73 Raster,
74 /// Copies, blits and fills.
75 Copy,
76 /// Acceleration structure builds.
77 Build,
78 /// Ray tracing pipeline traces.
79 RayTracing,
80 };
81
82 /// @brief How a shader reaches a resource. The graph treats all alike; the distinction
83 /// documents accesses no descriptor shows.
84 enum class Reach : std::uint8_t {
85 Descriptor,
86 /// An index into the bindless heap.
87 Bindless,
88 /// A buffer device address.
89 Address,
90 };
91
92 /// @brief A range of array layers; mips are always whole.
93 struct Layers {
94 std::uint32_t first = 0;
95 /// The layer count; `kAll` for the rest of the image.
96 std::uint32_t count = kAll;
97 static constexpr std::uint32_t kAll = ~0u;
98 };
99
100 /// @brief Where a resource's state comes from when a graph starts.
101 enum class Origin : std::uint8_t {
102 /// Owned outside the graph; written by anything earlier.
103 Imported,
104 /// A new transient: nothing touched it.
105 Fresh,
106 /// A pooled transient an earlier graph used.
107 Recycled,
108 };
109
110 /// @brief A resource as dependency analysis sees it.
111
1/2
✓ Branch 1 taken 20 times.
✗ Branch 2 not taken.
1695 struct DependencyResource {
112 std::string name;
113 bool image = false;
114 Origin origin = Origin::Fresh;
115 };
116
117 /// @brief One declared access of a pass.
118 struct DependencyAccess {
119 std::uint32_t resource = 0;
120 Use use = Use::Sampled;
121 Layers layers;
122 Reach reach = Reach::Descriptor;
123 };
124
125 /// @brief A pass as dependency analysis sees it.
126 536 struct DependencyPass {
127 std::string name;
128 Kind kind = Kind::Compute;
129 std::vector<DependencyAccess> accesses;
130 };
131
132 /// @brief The stages, accesses and layout of one use.
133 struct UseState {
134 VkPipelineStageFlags2 stages = VK_PIPELINE_STAGE_2_NONE;
135 VkAccessFlags2 access = VK_ACCESS_2_NONE;
136 /// The image layout; undefined for buffers.
137 VkImageLayout layout = VK_IMAGE_LAYOUT_UNDEFINED;
138 bool writes = false;
139 bool reads = false;
140 };
141
142 /// @brief A barrier for one resource: an image barrier for an image, folded into the pass's
143 /// memory barrier for a buffer.
144 struct Barrier {
145 std::uint32_t resource = 0;
146 VkPipelineStageFlags2 srcStages = VK_PIPELINE_STAGE_2_NONE;
147 VkAccessFlags2 srcAccess = VK_ACCESS_2_NONE;
148 VkPipelineStageFlags2 dstStages = VK_PIPELINE_STAGE_2_NONE;
149 VkAccessFlags2 dstAccess = VK_ACCESS_2_NONE;
150 /// An image's layouts before and after; equal for no transition.
151 VkImageLayout oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
152 VkImageLayout newLayout = VK_IMAGE_LAYOUT_UNDEFINED;
153 /// The image layers covered; whole for a layout change.
154 Layers layers;
155 };
156
157 /// @brief The barriers recorded together before a pass, or after the last.
158 536 struct Dependency {
159 std::vector<Barrier> images;
160 std::vector<Barrier> buffers;
161
162 287 bool Empty() const {
163
4/4
✓ Branch 0 taken 251 times.
✓ Branch 1 taken 36 times.
✓ Branch 2 taken 239 times.
✓ Branch 3 taken 12 times.
287 return images.empty() && buffers.empty();
164 }
165 };
166
167 /// @brief The dependencies of a whole graph.
168 47 struct PassDependencies {
169 /// The dependency before each pass, in order.
170 std::vector<Dependency> passes;
171 /// After the last pass: imported images back in the general layout, and imported writes
172 /// visible to later commands.
173 Dependency after;
174 /// Problems found; the result is unusable unless empty.
175 std::vector<std::string> errors;
176 };
177
178 /// @return The stages, accesses and layout of a use; no stages when `kind` cannot make it.
179 UseState ToState(Kind kind, Use use, bool image);
180
181 /// @brief The dependency before each pass, in order: a memory barrier from the last write for a
182 /// read, an execution dependency from earlier reads for a write, and an image barrier for a
183 /// layout change. Reads already made visible get none.
184 /// @return The dependencies, with errors for reads of unwritten transients, uses a resource
185 /// cannot have and conflicting layouts.
186 PassDependencies ComputeDependencies(std::span<const DependencyResource> resources,
187 std::span<const DependencyPass> passes);
188
189 /// @return A use's name, such as "StorageWrite".
190 const char* ToString(Use use);
191
192 } // namespace ernie::graph
193