/* * 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. */ #include "D3D12ResourceState.h" //---------------------------------------------------------------------------------------------------------------------------------- D3D12_RESOURCE_STATES CDesiredResourceState::GetSubresourceState(UINT SubresourceIndex) const { if (AreAllSubresourcesSame()) { SubresourceIndex = 0; } return m_spSubresourceStates[SubresourceIndex]; } //---------------------------------------------------------------------------------------------------------------------------------- void CDesiredResourceState::UpdateSubresourceState(unsigned SubresourceIndex, D3D12_RESOURCE_STATES state) { assert(SubresourceIndex < m_spSubresourceStates.size()); if (m_spSubresourceStates[SubresourceIndex] == UNKNOWN_RESOURCE_STATE || state == UNKNOWN_RESOURCE_STATE || IsD3D12WriteState(state)) { m_spSubresourceStates[SubresourceIndex] = state; } else { // Accumulate read state state bits m_spSubresourceStates[SubresourceIndex] |= state; } } //---------------------------------------------------------------------------------------------------------------------------------- void CDesiredResourceState::SetResourceState(D3D12_RESOURCE_STATES state) { m_bAllSubresourcesSame = true; UpdateSubresourceState(0, state); } //---------------------------------------------------------------------------------------------------------------------------------- void CDesiredResourceState::SetSubresourceState(UINT SubresourceIndex, D3D12_RESOURCE_STATES state) { if (m_bAllSubresourcesSame && m_spSubresourceStates.size() > 1) { std::fill(m_spSubresourceStates.begin() + 1, m_spSubresourceStates.end(), m_spSubresourceStates[0]); m_bAllSubresourcesSame = false; } if (m_spSubresourceStates.size() == 1) { SubresourceIndex = 0; } UpdateSubresourceState(SubresourceIndex, state); } //---------------------------------------------------------------------------------------------------------------------------------- void CDesiredResourceState::Reset() { SetResourceState(UNKNOWN_RESOURCE_STATE); } //---------------------------------------------------------------------------------------------------------------------------------- void CCurrentResourceState::ConvertToSubresourceTracking() { if (m_bAllSubresourcesSame && m_spLogicalState.size() > 1) { std::fill(m_spLogicalState.begin() + 1, m_spLogicalState.end(), m_spLogicalState[0]); m_bAllSubresourcesSame = false; } } //---------------------------------------------------------------------------------------------------------------------------------- CCurrentResourceState::CCurrentResourceState(UINT SubresourceCount, bool bSimultaneousAccess) : m_bSimultaneousAccess(bSimultaneousAccess) , m_spLogicalState(SubresourceCount) { m_spLogicalState[0] = LogicalState{}; } //---------------------------------------------------------------------------------------------------------------------------------- D3D12_RESOURCE_STATES CCurrentResourceState::StateIfPromoted(D3D12_RESOURCE_STATES State, UINT SubresourceIndex) { D3D12_RESOURCE_STATES Result = D3D12_RESOURCE_STATE_COMMON; if (m_bSimultaneousAccess || !!(State & ( D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE | D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE | D3D12_RESOURCE_STATE_COPY_SOURCE | D3D12_RESOURCE_STATE_COPY_DEST))) { auto CurState = GetLogicalSubresourceState(SubresourceIndex); // If the current state is COMMON... if(CurState.State == D3D12_RESOURCE_STATE_COMMON) { // ...then promotion is allowed Result = State; } // If the current state is a read state resulting from previous promotion... else if(CurState.IsPromotedState && !!(CurState.State & D3D12_RESOURCE_STATE_GENERIC_READ)) { // ...then (accumulated) promotion is allowed Result = State |= CurState.State; } } return Result; } //---------------------------------------------------------------------------------------------------------------------------------- void CCurrentResourceState::SetLogicalResourceState(LogicalState const& State) { m_bAllSubresourcesSame = true; m_spLogicalState[0] = State; } //---------------------------------------------------------------------------------------------------------------------------------- void CCurrentResourceState::SetLogicalSubresourceState(UINT SubresourceIndex, LogicalState const& State) { ConvertToSubresourceTracking(); m_spLogicalState[SubresourceIndex] = State; } //---------------------------------------------------------------------------------------------------------------------------------- auto CCurrentResourceState::GetLogicalSubresourceState(UINT SubresourceIndex) const -> LogicalState const& { if (AreAllSubresourcesSame()) { SubresourceIndex = 0; } return m_spLogicalState[SubresourceIndex]; } //---------------------------------------------------------------------------------------------------------------------------------- void CCurrentResourceState::Reset() { m_bAllSubresourcesSame = true; m_spLogicalState[0] = LogicalState{}; } //---------------------------------------------------------------------------------------------------------------------------------- ResourceStateManager::ResourceStateManager() { list_inithead(&m_TransitionListHead); // Reserve some space in these vectors upfront. Values are arbitrary. m_vResourceBarriers.reserve(50); } //---------------------------------------------------------------------------------------------------------------------------------- void ResourceStateManager::TransitionResource(TransitionableResourceState& Resource, D3D12_RESOURCE_STATES state) { Resource.m_DesiredState.SetResourceState(state); if (!Resource.IsTransitionPending()) { list_add(&Resource.m_TransitionListEntry, &m_TransitionListHead); } } //---------------------------------------------------------------------------------------------------------------------------------- void ResourceStateManager::TransitionSubresource(TransitionableResourceState& Resource, UINT SubresourceIndex, D3D12_RESOURCE_STATES state) { Resource.m_DesiredState.SetSubresourceState(SubresourceIndex, state); if (!Resource.IsTransitionPending()) { list_add(&Resource.m_TransitionListEntry, &m_TransitionListHead); } } //---------------------------------------------------------------------------------------------------------------------------------- void ResourceStateManager::ApplyResourceTransitionsPreamble() { m_vResourceBarriers.clear(); } //---------------------------------------------------------------------------------------------------------------------------------- /*static*/ bool ResourceStateManager::TransitionRequired(D3D12_RESOURCE_STATES CurrentState, D3D12_RESOURCE_STATES& DestinationState) { // An exact match never needs a transition. if (CurrentState == DestinationState) { return false; } if ( CurrentState == D3D12_RESOURCE_STATE_COMMON || DestinationState == D3D12_RESOURCE_STATE_COMMON) { return true; } // Current state already contains the destination state, we're good. if ((CurrentState & DestinationState) == DestinationState) { DestinationState = CurrentState; return false; } // If the transition involves a write state, then the destination should just be the requested destination. // Otherwise, accumulate read states to minimize future transitions (by triggering the above condition). if (!IsD3D12WriteState(DestinationState) && !IsD3D12WriteState(CurrentState)) { DestinationState |= CurrentState; } return true; } //---------------------------------------------------------------------------------------------------------------------------------- void ResourceStateManager::AddCurrentStateUpdate(TransitionableResourceState& Resource, CCurrentResourceState& CurrentState, UINT SubresourceIndex, const CCurrentResourceState::LogicalState &NewLogicalState) { if (SubresourceIndex == D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES) { CurrentState.SetLogicalResourceState(NewLogicalState); } else { CurrentState.SetLogicalSubresourceState(SubresourceIndex, NewLogicalState); } } //---------------------------------------------------------------------------------------------------------------------------------- void ResourceStateManager::ProcessTransitioningResource(ID3D12Resource* pTransitioningResource, TransitionableResourceState& TransitionableResourceState, CCurrentResourceState& CurrentState, UINT NumTotalSubresources, UINT64 ExecutionId) { // Figure out the set of subresources that are transitioning auto& DestinationState = TransitionableResourceState.m_DesiredState; bool bAllSubresourcesAtOnce = CurrentState.AreAllSubresourcesSame() && DestinationState.AreAllSubresourcesSame(); D3D12_RESOURCE_BARRIER TransitionDesc; memset(&TransitionDesc, 0, sizeof(TransitionDesc)); TransitionDesc.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; TransitionDesc.Transition.pResource = pTransitioningResource; UINT numSubresources = bAllSubresourcesAtOnce ? 1 : NumTotalSubresources; for (UINT i = 0; i < numSubresources; ++i) { D3D12_RESOURCE_STATES SubresourceDestinationState = DestinationState.GetSubresourceState(i); TransitionDesc.Transition.Subresource = bAllSubresourcesAtOnce ? D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES : i; // Is this subresource currently being used, or is it just being iterated over? D3D12_RESOURCE_STATES after = DestinationState.GetSubresourceState(i); if (after == UNKNOWN_RESOURCE_STATE) { // This subresource doesn't have any transition requested - move on to the next. continue; } ProcessTransitioningSubresourceExplicit( CurrentState, i, SubresourceDestinationState, after, TransitionableResourceState, TransitionDesc, ExecutionId); // throw( bad_alloc ) } // Update destination states. // Coalesce destination state to ensure that it's set for the entire resource. DestinationState.SetResourceState(UNKNOWN_RESOURCE_STATE); } //---------------------------------------------------------------------------------------------------------------------------------- void ResourceStateManager::ProcessTransitioningSubresourceExplicit( CCurrentResourceState& CurrentState, UINT SubresourceIndex, D3D12_RESOURCE_STATES state, D3D12_RESOURCE_STATES after, TransitionableResourceState& TransitionableResourceState, D3D12_RESOURCE_BARRIER& TransitionDesc, UINT64 ExecutionId) { // Simultaneous access resources currently in the COMMON // state can be implicitly promoted to any state other state. // Any non-simultaneous-access resources currently in the // COMMON state can still be implicitly promoted to SRV, // NON_PS_SRV, COPY_SRC, or COPY_DEST. CCurrentResourceState::LogicalState CurrentLogicalState = CurrentState.GetLogicalSubresourceState(SubresourceIndex); // If the last time this logical state was set was in a different // execution period and is decayable then decay the current state // to COMMON if(ExecutionId != CurrentLogicalState.ExecutionId && CurrentLogicalState.MayDecay) { CurrentLogicalState.State = D3D12_RESOURCE_STATE_COMMON; CurrentLogicalState.IsPromotedState = false; } bool MayDecay = false; bool IsPromotion = false; // If not promotable then StateIfPromoted will be D3D12_RESOURCE_STATE_COMMON auto StateIfPromoted = CurrentState.StateIfPromoted(after, SubresourceIndex); if ( D3D12_RESOURCE_STATE_COMMON == StateIfPromoted ) { if (TransitionRequired(CurrentLogicalState.State, /*inout*/ after)) { // Insert a single concrete barrier (for non-simultaneous access resources). TransitionDesc.Transition.StateBefore = D3D12_RESOURCE_STATES(CurrentLogicalState.State); TransitionDesc.Transition.StateAfter = D3D12_RESOURCE_STATES(after); assert(TransitionDesc.Transition.StateBefore != TransitionDesc.Transition.StateAfter); m_vResourceBarriers.push_back(TransitionDesc); // throw( bad_alloc ) MayDecay = CurrentState.SupportsSimultaneousAccess() && !IsD3D12WriteState(after); IsPromotion = false; } } else { // Handle identity state transition if(after != StateIfPromoted) { after = StateIfPromoted; MayDecay = !IsD3D12WriteState(after); IsPromotion = true; } } CCurrentResourceState::LogicalState NewLogicalState{after, ExecutionId, IsPromotion, MayDecay}; AddCurrentStateUpdate(TransitionableResourceState, CurrentState, TransitionDesc.Transition.Subresource, NewLogicalState); } //---------------------------------------------------------------------------------------------------------------------------------- void ResourceStateManager::SubmitResourceTransitions(ID3D12GraphicsCommandList *pCommandList) { // Submit any pending barriers on source command lists that are not the destination. if (!m_vResourceBarriers.empty()) { pCommandList->ResourceBarrier((UINT)m_vResourceBarriers.size(), m_vResourceBarriers.data()); } } //---------------------------------------------------------------------------------------------------------------------------------- void ResourceStateManager::TransitionResource(TransitionableResourceState* pResource, D3D12_RESOURCE_STATES State) { ResourceStateManager::TransitionResource(*pResource, State); } //---------------------------------------------------------------------------------------------------------------------------------- void ResourceStateManager::TransitionSubresource(TransitionableResourceState* pResource, UINT SubresourceIndex, D3D12_RESOURCE_STATES State) { ResourceStateManager::TransitionSubresource(*pResource, SubresourceIndex, State); } //---------------------------------------------------------------------------------------------------------------------------------- void ResourceStateManager::ApplyAllResourceTransitions(ID3D12GraphicsCommandList *pCommandList, UINT64 ExecutionId) { ApplyResourceTransitionsPreamble(); ForEachTransitioningResource([=](TransitionableResourceState& ResourceBase) { TransitionableResourceState& CurResource = static_cast(ResourceBase); ID3D12Resource *pResource = CurResource.GetD3D12Resource(); ProcessTransitioningResource( pResource, CurResource, CurResource.GetCurrentState(), CurResource.NumSubresources(), ExecutionId); }); SubmitResourceTransitions(pCommandList); }