1 /*
2 * Copyright © Microsoft Corporation
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
13 * Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21 * IN THE SOFTWARE.
22 */
23
24 #ifndef D3D12_RESOURCE_STATE_H
25 #define D3D12_RESOURCE_STATE_H
26
27 #ifndef _WIN32
28 #include <wsl/winadapter.h>
29 #endif
30
31 #include <vector>
32 #include <assert.h>
33 #include <directx/d3d12.h>
34
35 #include "util/list.h"
36
37 #if defined(__GNUC__)
38 #pragma GCC diagnostic ignored "-Winvalid-offsetof"
39 #endif
40
41 #define UNKNOWN_RESOURCE_STATE (D3D12_RESOURCE_STATES)0x8000u
42 #define RESOURCE_STATE_VALID_BITS 0x2f3fff
43 #define RESOURCE_STATE_VALID_INTERNAL_BITS 0x2fffff
44 constexpr D3D12_RESOURCE_STATES RESOURCE_STATE_ALL_WRITE_BITS =
45 D3D12_RESOURCE_STATE_RENDER_TARGET |
46 D3D12_RESOURCE_STATE_UNORDERED_ACCESS |
47 D3D12_RESOURCE_STATE_DEPTH_WRITE |
48 D3D12_RESOURCE_STATE_STREAM_OUT |
49 D3D12_RESOURCE_STATE_COPY_DEST |
50 D3D12_RESOURCE_STATE_RESOLVE_DEST |
51 D3D12_RESOURCE_STATE_VIDEO_DECODE_WRITE |
52 D3D12_RESOURCE_STATE_VIDEO_PROCESS_WRITE;
53
54 //---------------------------------------------------------------------------------------------------------------------------------
IsD3D12WriteState(UINT State)55 inline bool IsD3D12WriteState(UINT State)
56 {
57 return (State & RESOURCE_STATE_ALL_WRITE_BITS) != 0;
58 }
59
SupportsSimultaneousAccess(const D3D12_RESOURCE_DESC & desc)60 inline bool SupportsSimultaneousAccess(const D3D12_RESOURCE_DESC &desc)
61 {
62 return D3D12_RESOURCE_DIMENSION_BUFFER == desc.Dimension ||
63 !!(desc.Flags & D3D12_RESOURCE_FLAG_ALLOW_SIMULTANEOUS_ACCESS);
64 }
65
66 //==================================================================================================================================
67 // CDesiredResourceState
68 // Stores the current desired state of either an entire resource, or each subresource.
69 //==================================================================================================================================
70 class CDesiredResourceState
71 {
72 private:
73 bool m_bAllSubresourcesSame = true;
74
75 std::vector<D3D12_RESOURCE_STATES> m_spSubresourceStates;
76
77 public:
CDesiredResourceState(UINT SubresourceCount)78 CDesiredResourceState(UINT SubresourceCount) :
79 m_spSubresourceStates(SubresourceCount)
80 {
81 }
82
AreAllSubresourcesSame()83 bool AreAllSubresourcesSame() const { return m_bAllSubresourcesSame; }
84
85 D3D12_RESOURCE_STATES GetSubresourceState(UINT SubresourceIndex) const;
86 void SetResourceState(D3D12_RESOURCE_STATES state);
87 void SetSubresourceState(UINT SubresourceIndex, D3D12_RESOURCE_STATES state);
88
89 void Reset();
90
91 private:
92 void UpdateSubresourceState(unsigned SubresourceIndex, D3D12_RESOURCE_STATES state);
93 };
94
95 //==================================================================================================================================
96 // CCurrentResourceState
97 // Stores the current state of either an entire resource, or each subresource.
98 // Current state can either be shared read across multiple queues, or exclusive on a single queue.
99 //==================================================================================================================================
100 class CCurrentResourceState
101 {
102 public:
103 struct LogicalState
104 {
105 D3D12_RESOURCE_STATES State = D3D12_RESOURCE_STATE_COMMON;
106 UINT64 ExecutionId = 0;
107 bool IsPromotedState = false;
108 bool MayDecay = false;
109 };
110
111 private:
112 const bool m_bSimultaneousAccess;
113 bool m_bAllSubresourcesSame = true;
114
115 std::vector<LogicalState> m_spLogicalState;
116
117 void ConvertToSubresourceTracking();
118
119 public:
120 CCurrentResourceState(UINT SubresourceCount, bool bSimultaneousAccess);
121
SupportsSimultaneousAccess()122 bool SupportsSimultaneousAccess() const { return m_bSimultaneousAccess; }
123
124 // Returns the destination state if the current state is promotable.
125 // Returns D3D12_RESOURCE_STATE_COMMON if not.
126 D3D12_RESOURCE_STATES StateIfPromoted(D3D12_RESOURCE_STATES state, UINT SubresourceIndex);
127
AreAllSubresourcesSame()128 bool AreAllSubresourcesSame() const { return m_bAllSubresourcesSame; }
129
130 void SetLogicalResourceState(LogicalState const& State);
131 void SetLogicalSubresourceState(UINT SubresourceIndex, LogicalState const& State);
132 LogicalState const& GetLogicalSubresourceState(UINT SubresourceIndex) const;
133
134 void Reset();
135 };
136
137 //==================================================================================================================================
138 // TransitionableResourceState
139 // A base class that transitionable resources should inherit from.
140 //==================================================================================================================================
141 struct TransitionableResourceState
142 {
143 struct list_head m_TransitionListEntry;
144 CDesiredResourceState m_DesiredState;
145
TransitionableResourceStateTransitionableResourceState146 TransitionableResourceState(ID3D12Resource *pResource, UINT TotalSubresources, bool SupportsSimultaneousAccess) :
147 m_DesiredState(TotalSubresources),
148 m_TotalSubresources(TotalSubresources),
149 m_currentState(TotalSubresources, SupportsSimultaneousAccess),
150 m_pResource(pResource)
151 {
152 list_inithead(&m_TransitionListEntry);
153 }
154
~TransitionableResourceStateTransitionableResourceState155 ~TransitionableResourceState()
156 {
157 if (IsTransitionPending())
158 {
159 list_del(&m_TransitionListEntry);
160 }
161 }
162
IsTransitionPendingTransitionableResourceState163 bool IsTransitionPending() const { return !list_is_empty(&m_TransitionListEntry); }
164
NumSubresourcesTransitionableResourceState165 UINT NumSubresources() { return m_TotalSubresources; }
166
GetCurrentStateTransitionableResourceState167 CCurrentResourceState& GetCurrentState() { return m_currentState; }
168
GetD3D12ResourceTransitionableResourceState169 inline ID3D12Resource* GetD3D12Resource() const { return m_pResource; }
170
171 private:
172 unsigned m_TotalSubresources;
173
174 CCurrentResourceState m_currentState;
175
176 ID3D12Resource* m_pResource;
177 };
178
179 //==================================================================================================================================
180 // ResourceStateManager
181 // The main business logic for handling resource transitions, including multi-queue sync and shared/exclusive state changes.
182 //
183 // Requesting a resource to transition simply updates destination state, and ensures it's in a list to be processed later.
184 //
185 // When processing ApplyAllResourceTransitions, we build up sets of vectors.
186 // There's a source one for each command list type, and a single one for the dest because we are applying
187 // the resource transitions for a single operation.
188 // There's also a vector for "tentative" barriers, which are merged into the destination vector if
189 // no flushing occurs as a result of submitting the final barrier operation.
190 // 99% of the time, there will only be the source being populated, but sometimes there will be a destination as well.
191 // If the source and dest of a transition require different types, we put a (source->COMMON) in the approriate source vector,
192 // and a (COMMON->dest) in the destination vector.
193 //
194 // Once all resources are processed, we:
195 // 1. Submit all source barriers, except ones belonging to the destination queue.
196 // 2. Flush all source command lists, except ones belonging to the destination queue.
197 // 3. Determine if the destination queue is going to be flushed.
198 // If so: Submit source barriers on that command list first, then flush it.
199 // If not: Accumulate source, dest, and tentative barriers so they can be sent to D3D12 in a single API call.
200 // 4. Insert waits on the destination queue - deferred waits, and waits for work on other queues.
201 // 5. Insert destination barriers.
202 //
203 // Only once all of this has been done do we update the "current" state of resources,
204 // because this is the only way that we know whether or not the destination queue has been flushed,
205 // and therefore, we can get the correct fence values to store in the subresources.
206 //==================================================================================================================================
207 class ResourceStateManager
208 {
209 protected:
210
211 struct list_head m_TransitionListHead;
212
213 std::vector<D3D12_RESOURCE_BARRIER> m_vResourceBarriers;
214
215 public:
216 ResourceStateManager();
217
~ResourceStateManager()218 ~ResourceStateManager()
219 {
220 // All resources should be gone by this point, and each resource ensures it is no longer in this list.
221 assert(list_is_empty(&m_TransitionListHead));
222 }
223
224 // Call the D3D12 APIs to perform the resource barriers, command list submission, and command queue sync
225 // that was determined by previous calls to ProcessTransitioningResource.
226 void SubmitResourceTransitions(ID3D12GraphicsCommandList *pCommandList);
227
228 // Transition the entire resource to a particular destination state on a particular command list.
229 void TransitionResource(TransitionableResourceState* pResource,
230 D3D12_RESOURCE_STATES State);
231 // Transition a single subresource to a particular destination state.
232 void TransitionSubresource(TransitionableResourceState* pResource,
233 UINT SubresourceIndex,
234 D3D12_RESOURCE_STATES State);
235
236 // Submit all barriers and queue sync.
237 void ApplyAllResourceTransitions(ID3D12GraphicsCommandList *pCommandList, UINT64 ExecutionId);
238
239 private:
240 // These methods set the destination state of the resource/subresources and ensure it's in the transition list.
241 void TransitionResource(TransitionableResourceState& Resource,
242 D3D12_RESOURCE_STATES State);
243 void TransitionSubresource(TransitionableResourceState& Resource,
244 UINT SubresourceIndex,
245 D3D12_RESOURCE_STATES State);
246
247 // Clear out any state from previous iterations.
248 void ApplyResourceTransitionsPreamble();
249
250 // What to do with the resource, in the context of the transition list, after processing it.
251 enum class TransitionResult
252 {
253 // There are no more pending transitions that may be processed at a later time (i.e. draw time),
254 // so remove it from the pending transition list.
255 Remove,
256 // There are more transitions to be done, so keep it in the list.
257 Keep
258 };
259
260 // For every entry in the transition list, call a routine.
261 // This routine must return a TransitionResult which indicates what to do with the list.
262 template <typename TFunc>
ForEachTransitioningResource(TFunc && func)263 void ForEachTransitioningResource(TFunc&& func)
264 {
265 list_for_each_entry_safe(TransitionableResourceState, pResource, &m_TransitionListHead, m_TransitionListEntry)
266 {
267 func(*pResource);
268 list_delinit(&pResource->m_TransitionListEntry);
269 }
270 }
271
272 // Updates vectors with the operations that should be applied to the requested resource.
273 // May update the destination state of the resource.
274 void ProcessTransitioningResource(ID3D12Resource* pTransitioningResource,
275 TransitionableResourceState& TransitionableResourceState,
276 CCurrentResourceState& CurrentState,
277 UINT NumTotalSubresources,
278 UINT64 ExecutionId);
279
280 private:
281 // Helpers
282 static bool TransitionRequired(D3D12_RESOURCE_STATES CurrentState, D3D12_RESOURCE_STATES& DestinationState);
283 void AddCurrentStateUpdate(TransitionableResourceState& Resource,
284 CCurrentResourceState& CurrentState,
285 UINT SubresourceIndex,
286 const CCurrentResourceState::LogicalState &NewLogicalState);
287 void ProcessTransitioningSubresourceExplicit(CCurrentResourceState& CurrentState,
288 UINT i,
289 D3D12_RESOURCE_STATES state,
290 D3D12_RESOURCE_STATES after,
291 TransitionableResourceState& TransitionableResourceState,
292 D3D12_RESOURCE_BARRIER& TransitionDesc,
293 UINT64 ExecutionId);
294 };
295
296 #endif // D3D12_RESOURCE_STATE_H
297