D3D12ResourceState.h 13.3 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
/*
 * Copyright © Microsoft Corporation
 *
 * 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 (including the next
 * paragraph) 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.
 */

#ifndef D3D12_RESOURCE_STATE_H
#define D3D12_RESOURCE_STATE_H

#include <vector>
#include <assert.h>
#include <d3d12.h>

31 32
#include "util/list.h"

33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
#include "D3D12StateTransitionFlags.h"

#define UNKNOWN_RESOURCE_STATE (D3D12_RESOURCE_STATES)0x8000u
#define RESOURCE_STATE_VALID_BITS 0x2f3fff
#define RESOURCE_STATE_VALID_INTERNAL_BITS 0x2fffff
constexpr D3D12_RESOURCE_STATES RESOURCE_STATE_ALL_WRITE_BITS =
D3D12_RESOURCE_STATE_RENDER_TARGET          |
D3D12_RESOURCE_STATE_UNORDERED_ACCESS       |
D3D12_RESOURCE_STATE_DEPTH_WRITE            |
D3D12_RESOURCE_STATE_STREAM_OUT             |
D3D12_RESOURCE_STATE_COPY_DEST              |
D3D12_RESOURCE_STATE_RESOLVE_DEST           |
D3D12_RESOURCE_STATE_VIDEO_DECODE_WRITE     |
D3D12_RESOURCE_STATE_VIDEO_PROCESS_WRITE;

//---------------------------------------------------------------------------------------------------------------------------------
inline bool IsD3D12WriteState(UINT State, SubresourceTransitionFlags Flags)
{
51
   return (State & RESOURCE_STATE_ALL_WRITE_BITS) != 0;
52 53
}

54 55 56 57 58 59
inline bool SupportsSimultaneousAccess(const D3D12_RESOURCE_DESC &desc)
{
   return D3D12_RESOURCE_DIMENSION_BUFFER == desc.Dimension ||
          !!(desc.Flags & D3D12_RESOURCE_FLAG_ALLOW_SIMULTANEOUS_ACCESS);
}

60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92
//==================================================================================================================================
// CDesiredResourceState
// Stores the current desired state of either an entire resource, or each subresource.
//==================================================================================================================================
class CDesiredResourceState
{
public:
   struct SubresourceInfo
   {
      D3D12_RESOURCE_STATES State = UNKNOWN_RESOURCE_STATE;
      SubresourceTransitionFlags Flags = SubresourceTransitionFlags_None;
   };

private:
   bool m_bAllSubresourcesSame = true;

   std::vector<SubresourceInfo> m_spSubresourceInfo;

public:
   CDesiredResourceState(UINT SubresourceCount) :
      m_spSubresourceInfo(SubresourceCount)
   {
   }

   bool AreAllSubresourcesSame() const { return m_bAllSubresourcesSame; }

   SubresourceInfo const& GetSubresourceInfo(UINT SubresourceIndex) const;
   void SetResourceState(SubresourceInfo const& Info);
   void SetSubresourceState(UINT SubresourceIndex, SubresourceInfo const& Info);

   void Reset();

private:
93
   void UpdateSubresourceState(unsigned SubresourceIndex, SubresourceInfo const& info);
94 95 96 97 98 99 100 101 102 103
};

//==================================================================================================================================
// CCurrentResourceState
// Stores the current state of either an entire resource, or each subresource.
// Current state can either be shared read across multiple queues, or exclusive on a single queue.
//==================================================================================================================================
class CCurrentResourceState
{
public:
104
   struct LogicalState
105 106
   {
      D3D12_RESOURCE_STATES State = D3D12_RESOURCE_STATE_COMMON;
107 108 109
      UINT64 ExecutionId = 0;
      bool IsPromotedState = false;
      bool MayDecay = false;
110 111 112 113 114 115
   };

private:
   const bool m_bSimultaneousAccess;
   bool m_bAllSubresourcesSame = true;

116
   std::vector<LogicalState> m_spLogicalState;
117 118 119 120 121 122

   void ConvertToSubresourceTracking();

public:
   CCurrentResourceState(UINT SubresourceCount, bool bSimultaneousAccess);

123 124 125 126 127 128
   bool SupportsSimultaneousAccess() const { return m_bSimultaneousAccess; }

   // Returns the destination state if the current state is promotable.
   // Returns D3D12_RESOURCE_STATE_COMMON if not.
   D3D12_RESOURCE_STATES StateIfPromoted(D3D12_RESOURCE_STATES State, UINT SubresourceIndex, SubresourceTransitionFlags Flags);

129 130
   bool AreAllSubresourcesSame() const { return m_bAllSubresourcesSame; }

131 132 133
   void SetLogicalResourceState(LogicalState const& State);
   void SetLogicalSubresourceState(UINT SubresourceIndex, LogicalState const& State);
   LogicalState const& GetLogicalSubresourceState(UINT SubresourceIndex) const;
134 135 136 137 138 139 140 141 142 143

   void Reset();
};
    
//==================================================================================================================================
// TransitionableResourceState
// A base class that transitionable resources should inherit from.
//==================================================================================================================================
struct TransitionableResourceState
{
144
   struct list_head m_TransitionListEntry;
145 146
   CDesiredResourceState m_DesiredState;

147 148 149 150
   TransitionableResourceState(ID3D12Resource *pResource, UINT TotalSubresources, bool SupportsSimultaneousAccess) :
      m_DesiredState(TotalSubresources),
      m_TotalSubresources(TotalSubresources),
      m_currentState(TotalSubresources, SupportsSimultaneousAccess),
151 152
      m_pResource(pResource)
   {
153
      list_inithead(&m_TransitionListEntry);
154 155 156 157 158 159
   }

   ~TransitionableResourceState()
   {
      if (IsTransitionPending())
      {
160
         list_del(&m_TransitionListEntry);
161 162 163
      }
   }

164
   bool IsTransitionPending() const { return !list_is_empty(&m_TransitionListEntry); }
165

166
   UINT NumSubresources() { return m_TotalSubresources; }
167 168 169 170 171 172

   CCurrentResourceState& GetCurrentState() { return m_currentState; }

   inline ID3D12Resource* GetD3D12Resource() const { return m_pResource; }

private:
173 174
   unsigned m_TotalSubresources;
   bool m_SupportsSimultaneousAccess;
175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212

   CCurrentResourceState m_currentState;

   ID3D12Resource* m_pResource;
};

//==================================================================================================================================
// ResourceStateManager
// The main business logic for handling resource transitions, including multi-queue sync and shared/exclusive state changes.
//
// Requesting a resource to transition simply updates destination state, and ensures it's in a list to be processed later.
//
// When processing ApplyAllResourceTransitions, we build up sets of vectors.
// There's a source one for each command list type, and a single one for the dest because we are applying
// the resource transitions for a single operation.
// There's also a vector for "tentative" barriers, which are merged into the destination vector if
// no flushing occurs as a result of submitting the final barrier operation.
// 99% of the time, there will only be the source being populated, but sometimes there will be a destination as well.
// If the source and dest of a transition require different types, we put a (source->COMMON) in the approriate source vector,
// and a (COMMON->dest) in the destination vector.
//
// Once all resources are processed, we:
// 1. Submit all source barriers, except ones belonging to the destination queue.
// 2. Flush all source command lists, except ones belonging to the destination queue.
// 3. Determine if the destination queue is going to be flushed.
//    If so: Submit source barriers on that command list first, then flush it.
//    If not: Accumulate source, dest, and tentative barriers so they can be sent to D3D12 in a single API call.
// 4. Insert waits on the destination queue - deferred waits, and waits for work on other queues.
// 5. Insert destination barriers.
//
// Only once all of this has been done do we update the "current" state of resources,
// because this is the only way that we know whether or not the destination queue has been flushed,
// and therefore, we can get the correct fence values to store in the subresources.
//==================================================================================================================================
class ResourceStateManager
{
protected:

213
   struct list_head m_TransitionListHead;
214 215 216 217 218 219 220 221 222

   std::vector<D3D12_RESOURCE_BARRIER> m_vResourceBarriers;

public:
   ResourceStateManager();

   ~ResourceStateManager()
   {
      // All resources should be gone by this point, and each resource ensures it is no longer in this list.
223
      assert(list_is_empty(&m_TransitionListHead));
224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240
   }

   // Call the D3D12 APIs to perform the resource barriers, command list submission, and command queue sync
   // that was determined by previous calls to ProcessTransitioningResource.
   void SubmitResourceTransitions(ID3D12GraphicsCommandList *pCommandList);

   // Transition the entire resource to a particular destination state on a particular command list.
   void TransitionResource(TransitionableResourceState* pResource,
                           D3D12_RESOURCE_STATES State,
                           SubresourceTransitionFlags Flags = SubresourceTransitionFlags_None);
   // Transition a single subresource to a particular destination state.
   void TransitionSubresource(TransitionableResourceState* pResource,
                              UINT SubresourceIndex,
                              D3D12_RESOURCE_STATES State,
                              SubresourceTransitionFlags Flags = SubresourceTransitionFlags_None);

   // Submit all barriers and queue sync.
241
   void ApplyAllResourceTransitions(ID3D12GraphicsCommandList *pCommandList, UINT64 ExecutionId, bool bIsPreDraw = false);
242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268

private:
   // These methods set the destination state of the resource/subresources and ensure it's in the transition list.
   void TransitionResource(TransitionableResourceState& Resource,
                           CDesiredResourceState::SubresourceInfo const& State);
   void TransitionSubresource(TransitionableResourceState& Resource,
                              UINT SubresourceIndex,
                              CDesiredResourceState::SubresourceInfo const& State);

   // Clear out any state from previous iterations.
   void ApplyResourceTransitionsPreamble();

   // What to do with the resource, in the context of the transition list, after processing it.
   enum class TransitionResult
   {
      // There are no more pending transitions that may be processed at a later time (i.e. draw time),
      // so remove it from the pending transition list.
      Remove,
      // There are more transitions to be done, so keep it in the list.
      Keep
   };

   // For every entry in the transition list, call a routine.
   // This routine must return a TransitionResult which indicates what to do with the list.
   template <typename TFunc>
   void ForEachTransitioningResource(TFunc&& func)
   {
269
      list_for_each_entry_safe(TransitionableResourceState, pResource, &m_TransitionListHead, m_TransitionListEntry)
270 271 272 273 274
      {
            TransitionResult result = func(*pResource);

            if (result == TransitionResult::Remove)
            {
275
               list_delinit(&pResource->m_TransitionListEntry);
276 277 278 279 280 281 282 283 284 285
            }
      }
   }

   // Updates vectors with the operations that should be applied to the requested resource.
   // May update the destination state of the resource.
   TransitionResult ProcessTransitioningResource(ID3D12Resource* pTransitioningResource,
                                                TransitionableResourceState& TransitionableResourceState,
                                                CCurrentResourceState& CurrentState,
                                                UINT NumTotalSubresources,
286
                                                UINT64 ExecutionId,
287 288 289 290 291 292 293 294
                                                bool bIsPreDraw);

private:
   // Helpers
   static bool TransitionRequired(D3D12_RESOURCE_STATES CurrentState, D3D12_RESOURCE_STATES& DestinationState, SubresourceTransitionFlags Flags);
   void AddCurrentStateUpdate(TransitionableResourceState& Resource,
                              CCurrentResourceState& CurrentState,
                              UINT SubresourceIndex,
295
                              const CCurrentResourceState::LogicalState &NewLogicalState);
296 297 298 299 300 301
   void ProcessTransitioningSubresourceExplicit(CCurrentResourceState& CurrentState,
                                                UINT i,
                                                CDesiredResourceState::SubresourceInfo& SubresourceDestinationInfo,
                                                D3D12_RESOURCE_STATES after,
                                                TransitionableResourceState& TransitionableResourceState,
                                                D3D12_RESOURCE_BARRIER& TransitionDesc,
302 303
                                                SubresourceTransitionFlags Flags,
                                                UINT64 ExecutionId);
304 305 306
};

#endif // D3D12_RESOURCE_STATE_H