17ec681f3Smrg/*
27ec681f3Smrg * Copyright © Microsoft Corporation
37ec681f3Smrg *
47ec681f3Smrg * Permission is hereby granted, free of charge, to any person obtaining a
57ec681f3Smrg * copy of this software and associated documentation files (the "Software"),
67ec681f3Smrg * to deal in the Software without restriction, including without limitation
77ec681f3Smrg * the rights to use, copy, modify, merge, publish, distribute, sublicense,
87ec681f3Smrg * and/or sell copies of the Software, and to permit persons to whom the
97ec681f3Smrg * Software is furnished to do so, subject to the following conditions:
107ec681f3Smrg *
117ec681f3Smrg * The above copyright notice and this permission notice (including the next
127ec681f3Smrg * paragraph) shall be included in all copies or substantial portions of the
137ec681f3Smrg * Software.
147ec681f3Smrg *
157ec681f3Smrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
167ec681f3Smrg * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
177ec681f3Smrg * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
187ec681f3Smrg * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
197ec681f3Smrg * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
207ec681f3Smrg * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
217ec681f3Smrg * IN THE SOFTWARE.
227ec681f3Smrg */
237ec681f3Smrg
247ec681f3Smrg#ifndef D3D12_RESOURCE_STATE_H
257ec681f3Smrg#define D3D12_RESOURCE_STATE_H
267ec681f3Smrg
277ec681f3Smrg#ifndef _WIN32
287ec681f3Smrg#include <wsl/winadapter.h>
297ec681f3Smrg#endif
307ec681f3Smrg
317ec681f3Smrg#include <vector>
327ec681f3Smrg#include <assert.h>
337ec681f3Smrg#include <directx/d3d12.h>
347ec681f3Smrg
357ec681f3Smrg#include "util/list.h"
367ec681f3Smrg
377ec681f3Smrg#if defined(__GNUC__)
387ec681f3Smrg#pragma GCC diagnostic ignored "-Winvalid-offsetof"
397ec681f3Smrg#endif
407ec681f3Smrg
417ec681f3Smrg#define UNKNOWN_RESOURCE_STATE (D3D12_RESOURCE_STATES)0x8000u
427ec681f3Smrg#define RESOURCE_STATE_VALID_BITS 0x2f3fff
437ec681f3Smrg#define RESOURCE_STATE_VALID_INTERNAL_BITS 0x2fffff
447ec681f3Smrgconstexpr D3D12_RESOURCE_STATES RESOURCE_STATE_ALL_WRITE_BITS =
457ec681f3SmrgD3D12_RESOURCE_STATE_RENDER_TARGET          |
467ec681f3SmrgD3D12_RESOURCE_STATE_UNORDERED_ACCESS       |
477ec681f3SmrgD3D12_RESOURCE_STATE_DEPTH_WRITE            |
487ec681f3SmrgD3D12_RESOURCE_STATE_STREAM_OUT             |
497ec681f3SmrgD3D12_RESOURCE_STATE_COPY_DEST              |
507ec681f3SmrgD3D12_RESOURCE_STATE_RESOLVE_DEST           |
517ec681f3SmrgD3D12_RESOURCE_STATE_VIDEO_DECODE_WRITE     |
527ec681f3SmrgD3D12_RESOURCE_STATE_VIDEO_PROCESS_WRITE;
537ec681f3Smrg
547ec681f3Smrg//---------------------------------------------------------------------------------------------------------------------------------
557ec681f3Smrginline bool IsD3D12WriteState(UINT State)
567ec681f3Smrg{
577ec681f3Smrg   return (State & RESOURCE_STATE_ALL_WRITE_BITS) != 0;
587ec681f3Smrg}
597ec681f3Smrg
607ec681f3Smrginline bool SupportsSimultaneousAccess(const D3D12_RESOURCE_DESC &desc)
617ec681f3Smrg{
627ec681f3Smrg   return D3D12_RESOURCE_DIMENSION_BUFFER == desc.Dimension ||
637ec681f3Smrg          !!(desc.Flags & D3D12_RESOURCE_FLAG_ALLOW_SIMULTANEOUS_ACCESS);
647ec681f3Smrg}
657ec681f3Smrg
667ec681f3Smrg//==================================================================================================================================
677ec681f3Smrg// CDesiredResourceState
687ec681f3Smrg// Stores the current desired state of either an entire resource, or each subresource.
697ec681f3Smrg//==================================================================================================================================
707ec681f3Smrgclass CDesiredResourceState
717ec681f3Smrg{
727ec681f3Smrgprivate:
737ec681f3Smrg   bool m_bAllSubresourcesSame = true;
747ec681f3Smrg
757ec681f3Smrg   std::vector<D3D12_RESOURCE_STATES> m_spSubresourceStates;
767ec681f3Smrg
777ec681f3Smrgpublic:
787ec681f3Smrg   CDesiredResourceState(UINT SubresourceCount) :
797ec681f3Smrg      m_spSubresourceStates(SubresourceCount)
807ec681f3Smrg   {
817ec681f3Smrg   }
827ec681f3Smrg
837ec681f3Smrg   bool AreAllSubresourcesSame() const { return m_bAllSubresourcesSame; }
847ec681f3Smrg
857ec681f3Smrg   D3D12_RESOURCE_STATES GetSubresourceState(UINT SubresourceIndex) const;
867ec681f3Smrg   void SetResourceState(D3D12_RESOURCE_STATES state);
877ec681f3Smrg   void SetSubresourceState(UINT SubresourceIndex, D3D12_RESOURCE_STATES state);
887ec681f3Smrg
897ec681f3Smrg   void Reset();
907ec681f3Smrg
917ec681f3Smrgprivate:
927ec681f3Smrg   void UpdateSubresourceState(unsigned SubresourceIndex, D3D12_RESOURCE_STATES state);
937ec681f3Smrg};
947ec681f3Smrg
957ec681f3Smrg//==================================================================================================================================
967ec681f3Smrg// CCurrentResourceState
977ec681f3Smrg// Stores the current state of either an entire resource, or each subresource.
987ec681f3Smrg// Current state can either be shared read across multiple queues, or exclusive on a single queue.
997ec681f3Smrg//==================================================================================================================================
1007ec681f3Smrgclass CCurrentResourceState
1017ec681f3Smrg{
1027ec681f3Smrgpublic:
1037ec681f3Smrg   struct LogicalState
1047ec681f3Smrg   {
1057ec681f3Smrg      D3D12_RESOURCE_STATES State = D3D12_RESOURCE_STATE_COMMON;
1067ec681f3Smrg      UINT64 ExecutionId = 0;
1077ec681f3Smrg      bool IsPromotedState = false;
1087ec681f3Smrg      bool MayDecay = false;
1097ec681f3Smrg   };
1107ec681f3Smrg
1117ec681f3Smrgprivate:
1127ec681f3Smrg   const bool m_bSimultaneousAccess;
1137ec681f3Smrg   bool m_bAllSubresourcesSame = true;
1147ec681f3Smrg
1157ec681f3Smrg   std::vector<LogicalState> m_spLogicalState;
1167ec681f3Smrg
1177ec681f3Smrg   void ConvertToSubresourceTracking();
1187ec681f3Smrg
1197ec681f3Smrgpublic:
1207ec681f3Smrg   CCurrentResourceState(UINT SubresourceCount, bool bSimultaneousAccess);
1217ec681f3Smrg
1227ec681f3Smrg   bool SupportsSimultaneousAccess() const { return m_bSimultaneousAccess; }
1237ec681f3Smrg
1247ec681f3Smrg   // Returns the destination state if the current state is promotable.
1257ec681f3Smrg   // Returns D3D12_RESOURCE_STATE_COMMON if not.
1267ec681f3Smrg   D3D12_RESOURCE_STATES StateIfPromoted(D3D12_RESOURCE_STATES state, UINT SubresourceIndex);
1277ec681f3Smrg
1287ec681f3Smrg   bool AreAllSubresourcesSame() const { return m_bAllSubresourcesSame; }
1297ec681f3Smrg
1307ec681f3Smrg   void SetLogicalResourceState(LogicalState const& State);
1317ec681f3Smrg   void SetLogicalSubresourceState(UINT SubresourceIndex, LogicalState const& State);
1327ec681f3Smrg   LogicalState const& GetLogicalSubresourceState(UINT SubresourceIndex) const;
1337ec681f3Smrg
1347ec681f3Smrg   void Reset();
1357ec681f3Smrg};
1367ec681f3Smrg
1377ec681f3Smrg//==================================================================================================================================
1387ec681f3Smrg// TransitionableResourceState
1397ec681f3Smrg// A base class that transitionable resources should inherit from.
1407ec681f3Smrg//==================================================================================================================================
1417ec681f3Smrgstruct TransitionableResourceState
1427ec681f3Smrg{
1437ec681f3Smrg   struct list_head m_TransitionListEntry;
1447ec681f3Smrg   CDesiredResourceState m_DesiredState;
1457ec681f3Smrg
1467ec681f3Smrg   TransitionableResourceState(ID3D12Resource *pResource, UINT TotalSubresources, bool SupportsSimultaneousAccess) :
1477ec681f3Smrg      m_DesiredState(TotalSubresources),
1487ec681f3Smrg      m_TotalSubresources(TotalSubresources),
1497ec681f3Smrg      m_currentState(TotalSubresources, SupportsSimultaneousAccess),
1507ec681f3Smrg      m_pResource(pResource)
1517ec681f3Smrg   {
1527ec681f3Smrg      list_inithead(&m_TransitionListEntry);
1537ec681f3Smrg   }
1547ec681f3Smrg
1557ec681f3Smrg   ~TransitionableResourceState()
1567ec681f3Smrg   {
1577ec681f3Smrg      if (IsTransitionPending())
1587ec681f3Smrg      {
1597ec681f3Smrg         list_del(&m_TransitionListEntry);
1607ec681f3Smrg      }
1617ec681f3Smrg   }
1627ec681f3Smrg
1637ec681f3Smrg   bool IsTransitionPending() const { return !list_is_empty(&m_TransitionListEntry); }
1647ec681f3Smrg
1657ec681f3Smrg   UINT NumSubresources() { return m_TotalSubresources; }
1667ec681f3Smrg
1677ec681f3Smrg   CCurrentResourceState& GetCurrentState() { return m_currentState; }
1687ec681f3Smrg
1697ec681f3Smrg   inline ID3D12Resource* GetD3D12Resource() const { return m_pResource; }
1707ec681f3Smrg
1717ec681f3Smrgprivate:
1727ec681f3Smrg   unsigned m_TotalSubresources;
1737ec681f3Smrg
1747ec681f3Smrg   CCurrentResourceState m_currentState;
1757ec681f3Smrg
1767ec681f3Smrg   ID3D12Resource* m_pResource;
1777ec681f3Smrg};
1787ec681f3Smrg
1797ec681f3Smrg//==================================================================================================================================
1807ec681f3Smrg// ResourceStateManager
1817ec681f3Smrg// The main business logic for handling resource transitions, including multi-queue sync and shared/exclusive state changes.
1827ec681f3Smrg//
1837ec681f3Smrg// Requesting a resource to transition simply updates destination state, and ensures it's in a list to be processed later.
1847ec681f3Smrg//
1857ec681f3Smrg// When processing ApplyAllResourceTransitions, we build up sets of vectors.
1867ec681f3Smrg// There's a source one for each command list type, and a single one for the dest because we are applying
1877ec681f3Smrg// the resource transitions for a single operation.
1887ec681f3Smrg// There's also a vector for "tentative" barriers, which are merged into the destination vector if
1897ec681f3Smrg// no flushing occurs as a result of submitting the final barrier operation.
1907ec681f3Smrg// 99% of the time, there will only be the source being populated, but sometimes there will be a destination as well.
1917ec681f3Smrg// If the source and dest of a transition require different types, we put a (source->COMMON) in the approriate source vector,
1927ec681f3Smrg// and a (COMMON->dest) in the destination vector.
1937ec681f3Smrg//
1947ec681f3Smrg// Once all resources are processed, we:
1957ec681f3Smrg// 1. Submit all source barriers, except ones belonging to the destination queue.
1967ec681f3Smrg// 2. Flush all source command lists, except ones belonging to the destination queue.
1977ec681f3Smrg// 3. Determine if the destination queue is going to be flushed.
1987ec681f3Smrg//    If so: Submit source barriers on that command list first, then flush it.
1997ec681f3Smrg//    If not: Accumulate source, dest, and tentative barriers so they can be sent to D3D12 in a single API call.
2007ec681f3Smrg// 4. Insert waits on the destination queue - deferred waits, and waits for work on other queues.
2017ec681f3Smrg// 5. Insert destination barriers.
2027ec681f3Smrg//
2037ec681f3Smrg// Only once all of this has been done do we update the "current" state of resources,
2047ec681f3Smrg// because this is the only way that we know whether or not the destination queue has been flushed,
2057ec681f3Smrg// and therefore, we can get the correct fence values to store in the subresources.
2067ec681f3Smrg//==================================================================================================================================
2077ec681f3Smrgclass ResourceStateManager
2087ec681f3Smrg{
2097ec681f3Smrgprotected:
2107ec681f3Smrg
2117ec681f3Smrg   struct list_head m_TransitionListHead;
2127ec681f3Smrg
2137ec681f3Smrg   std::vector<D3D12_RESOURCE_BARRIER> m_vResourceBarriers;
2147ec681f3Smrg
2157ec681f3Smrgpublic:
2167ec681f3Smrg   ResourceStateManager();
2177ec681f3Smrg
2187ec681f3Smrg   ~ResourceStateManager()
2197ec681f3Smrg   {
2207ec681f3Smrg      // All resources should be gone by this point, and each resource ensures it is no longer in this list.
2217ec681f3Smrg      assert(list_is_empty(&m_TransitionListHead));
2227ec681f3Smrg   }
2237ec681f3Smrg
2247ec681f3Smrg   // Call the D3D12 APIs to perform the resource barriers, command list submission, and command queue sync
2257ec681f3Smrg   // that was determined by previous calls to ProcessTransitioningResource.
2267ec681f3Smrg   void SubmitResourceTransitions(ID3D12GraphicsCommandList *pCommandList);
2277ec681f3Smrg
2287ec681f3Smrg   // Transition the entire resource to a particular destination state on a particular command list.
2297ec681f3Smrg   void TransitionResource(TransitionableResourceState* pResource,
2307ec681f3Smrg                           D3D12_RESOURCE_STATES State);
2317ec681f3Smrg   // Transition a single subresource to a particular destination state.
2327ec681f3Smrg   void TransitionSubresource(TransitionableResourceState* pResource,
2337ec681f3Smrg                              UINT SubresourceIndex,
2347ec681f3Smrg                              D3D12_RESOURCE_STATES State);
2357ec681f3Smrg
2367ec681f3Smrg   // Submit all barriers and queue sync.
2377ec681f3Smrg   void ApplyAllResourceTransitions(ID3D12GraphicsCommandList *pCommandList, UINT64 ExecutionId);
2387ec681f3Smrg
2397ec681f3Smrgprivate:
2407ec681f3Smrg   // These methods set the destination state of the resource/subresources and ensure it's in the transition list.
2417ec681f3Smrg   void TransitionResource(TransitionableResourceState& Resource,
2427ec681f3Smrg                           D3D12_RESOURCE_STATES State);
2437ec681f3Smrg   void TransitionSubresource(TransitionableResourceState& Resource,
2447ec681f3Smrg                              UINT SubresourceIndex,
2457ec681f3Smrg                              D3D12_RESOURCE_STATES State);
2467ec681f3Smrg
2477ec681f3Smrg   // Clear out any state from previous iterations.
2487ec681f3Smrg   void ApplyResourceTransitionsPreamble();
2497ec681f3Smrg
2507ec681f3Smrg   // What to do with the resource, in the context of the transition list, after processing it.
2517ec681f3Smrg   enum class TransitionResult
2527ec681f3Smrg   {
2537ec681f3Smrg      // There are no more pending transitions that may be processed at a later time (i.e. draw time),
2547ec681f3Smrg      // so remove it from the pending transition list.
2557ec681f3Smrg      Remove,
2567ec681f3Smrg      // There are more transitions to be done, so keep it in the list.
2577ec681f3Smrg      Keep
2587ec681f3Smrg   };
2597ec681f3Smrg
2607ec681f3Smrg   // For every entry in the transition list, call a routine.
2617ec681f3Smrg   // This routine must return a TransitionResult which indicates what to do with the list.
2627ec681f3Smrg   template <typename TFunc>
2637ec681f3Smrg   void ForEachTransitioningResource(TFunc&& func)
2647ec681f3Smrg   {
2657ec681f3Smrg      list_for_each_entry_safe(TransitionableResourceState, pResource, &m_TransitionListHead, m_TransitionListEntry)
2667ec681f3Smrg      {
2677ec681f3Smrg            func(*pResource);
2687ec681f3Smrg            list_delinit(&pResource->m_TransitionListEntry);
2697ec681f3Smrg      }
2707ec681f3Smrg   }
2717ec681f3Smrg
2727ec681f3Smrg   // Updates vectors with the operations that should be applied to the requested resource.
2737ec681f3Smrg   // May update the destination state of the resource.
2747ec681f3Smrg   void ProcessTransitioningResource(ID3D12Resource* pTransitioningResource,
2757ec681f3Smrg                                     TransitionableResourceState& TransitionableResourceState,
2767ec681f3Smrg                                     CCurrentResourceState& CurrentState,
2777ec681f3Smrg                                     UINT NumTotalSubresources,
2787ec681f3Smrg                                     UINT64 ExecutionId);
2797ec681f3Smrg
2807ec681f3Smrgprivate:
2817ec681f3Smrg   // Helpers
2827ec681f3Smrg   static bool TransitionRequired(D3D12_RESOURCE_STATES CurrentState, D3D12_RESOURCE_STATES& DestinationState);
2837ec681f3Smrg   void AddCurrentStateUpdate(TransitionableResourceState& Resource,
2847ec681f3Smrg                              CCurrentResourceState& CurrentState,
2857ec681f3Smrg                              UINT SubresourceIndex,
2867ec681f3Smrg                              const CCurrentResourceState::LogicalState &NewLogicalState);
2877ec681f3Smrg   void ProcessTransitioningSubresourceExplicit(CCurrentResourceState& CurrentState,
2887ec681f3Smrg                                                UINT i,
2897ec681f3Smrg                                                D3D12_RESOURCE_STATES state,
2907ec681f3Smrg                                                D3D12_RESOURCE_STATES after,
2917ec681f3Smrg                                                TransitionableResourceState& TransitionableResourceState,
2927ec681f3Smrg                                                D3D12_RESOURCE_BARRIER& TransitionDesc,
2937ec681f3Smrg                                                UINT64 ExecutionId);
2947ec681f3Smrg};
2957ec681f3Smrg
2967ec681f3Smrg#endif // D3D12_RESOURCE_STATE_H
297