From 5cfb2763ae0bd7a7eff679fa19565690be1abeb7 Mon Sep 17 00:00:00 2001 From: assiduous Date: Sun, 12 Jan 2020 20:29:31 -0800 Subject: Added DurationQueryHelper class; fixed issue with disjoint query in D3D11 backend --- .../include/DisjointQueryPool.h | 1 + .../src/DeviceContextD3D11Impl.cpp | 2 + .../GraphicsEngineD3D11/src/QueryD3D11Impl.cpp | 26 +++-- .../GraphicsEngineD3D11/src/SwapChainD3D11Impl.cpp | 1 + Graphics/GraphicsTools/CMakeLists.txt | 2 + .../GraphicsTools/interface/DurationQueryHelper.h | 103 +++++++++++++++++++ Graphics/GraphicsTools/src/DurationQueryHelper.cpp | 114 +++++++++++++++++++++ 7 files changed, 238 insertions(+), 11 deletions(-) create mode 100644 Graphics/GraphicsTools/interface/DurationQueryHelper.h create mode 100644 Graphics/GraphicsTools/src/DurationQueryHelper.cpp (limited to 'Graphics') diff --git a/Graphics/GraphicsEngineD3D11/include/DisjointQueryPool.h b/Graphics/GraphicsEngineD3D11/include/DisjointQueryPool.h index 5de5cc2a..c9d259ab 100644 --- a/Graphics/GraphicsEngineD3D11/include/DisjointQueryPool.h +++ b/Graphics/GraphicsEngineD3D11/include/DisjointQueryPool.h @@ -42,6 +42,7 @@ public: { DisjointQueryPool& Pool; CComPtr pd3d11Query; + bool IsEnded = false; DisjointQueryWrapper(DisjointQueryPool& _Pool, CComPtr&& _pd3d11Query) : diff --git a/Graphics/GraphicsEngineD3D11/src/DeviceContextD3D11Impl.cpp b/Graphics/GraphicsEngineD3D11/src/DeviceContextD3D11Impl.cpp index 8009c9b0..d963c7b1 100755 --- a/Graphics/GraphicsEngineD3D11/src/DeviceContextD3D11Impl.cpp +++ b/Graphics/GraphicsEngineD3D11/src/DeviceContextD3D11Impl.cpp @@ -1152,6 +1152,7 @@ void DeviceContextD3D11Impl::FinishFrame() if (m_ActiveDisjointQuery) { m_pd3d11DeviceContext->End(m_ActiveDisjointQuery->pd3d11Query); + m_ActiveDisjointQuery->IsEnded = true; m_ActiveDisjointQuery.reset(); } } @@ -1838,6 +1839,7 @@ void DeviceContextD3D11Impl::EndQuery(IQuery* pQuery) m_ActiveDisjointQuery = m_DisjointQueryPool.GetDisjointQuery(m_pDevice->GetD3D11Device()); // Disjoint timestamp queries should only be invoked once per frame or less. m_pd3d11DeviceContext->Begin(m_ActiveDisjointQuery->pd3d11Query); + m_ActiveDisjointQuery->IsEnded = false; } pQueryD3D11Impl->SetDisjointQuery(m_ActiveDisjointQuery); } diff --git a/Graphics/GraphicsEngineD3D11/src/QueryD3D11Impl.cpp b/Graphics/GraphicsEngineD3D11/src/QueryD3D11Impl.cpp index a6335b13..751810de 100644 --- a/Graphics/GraphicsEngineD3D11/src/QueryD3D11Impl.cpp +++ b/Graphics/GraphicsEngineD3D11/src/QueryD3D11Impl.cpp @@ -119,20 +119,24 @@ bool QueryD3D11Impl::GetData(void* pData, Uint32 DataSize, bool AutoInvalidate) VERIFY_EXPR(m_DisjointQuery); - UINT64 Counter = 0; - DataReady = pd3d11Ctx->GetData(m_pd3d11Query, &Counter, sizeof(Counter), 0) == S_OK; - - if (DataReady && pData != nullptr) + DataReady = m_DisjointQuery->IsEnded; + if (DataReady) { - D3D11_QUERY_DATA_TIMESTAMP_DISJOINT DisjointQueryData; - DataReady = pd3d11Ctx->GetData(m_DisjointQuery->pd3d11Query, &DisjointQueryData, sizeof(DisjointQueryData), 0) == S_OK; + UINT64 Counter = 0; + DataReady = pd3d11Ctx->GetData(m_pd3d11Query, &Counter, sizeof(Counter), 0) == S_OK; - if (DataReady) + if (DataReady && pData != nullptr) { - auto& QueryData = *reinterpret_cast(pData); - QueryData.Counter = Counter; - // The timestamp returned by ID3D11DeviceContext::GetData for a timestamp query is only reliable if Disjoint is FALSE. - QueryData.Frequency = DisjointQueryData.Disjoint ? 0 : DisjointQueryData.Frequency; + D3D11_QUERY_DATA_TIMESTAMP_DISJOINT DisjointQueryData; + DataReady = pd3d11Ctx->GetData(m_DisjointQuery->pd3d11Query, &DisjointQueryData, sizeof(DisjointQueryData), 0) == S_OK; + + if (DataReady) + { + auto& QueryData = *reinterpret_cast(pData); + QueryData.Counter = Counter; + // The timestamp returned by ID3D11DeviceContext::GetData for a timestamp query is only reliable if Disjoint is FALSE. + QueryData.Frequency = DisjointQueryData.Disjoint ? 0 : DisjointQueryData.Frequency; + } } } } diff --git a/Graphics/GraphicsEngineD3D11/src/SwapChainD3D11Impl.cpp b/Graphics/GraphicsEngineD3D11/src/SwapChainD3D11Impl.cpp index 94497ca0..537064e5 100644 --- a/Graphics/GraphicsEngineD3D11/src/SwapChainD3D11Impl.cpp +++ b/Graphics/GraphicsEngineD3D11/src/SwapChainD3D11Impl.cpp @@ -133,6 +133,7 @@ void SwapChainD3D11Impl::Present(Uint32 SyncInterval) if (m_SwapChainDesc.IsPrimary) { + pImmediateCtxD3D11->FinishFrame(); // Clear the state caches to release all outstanding objects // that are only kept alive by references in the cache // It is better to do this before calling Present() as D3D11 diff --git a/Graphics/GraphicsTools/CMakeLists.txt b/Graphics/GraphicsTools/CMakeLists.txt index 66b7a1ef..4c61df4b 100644 --- a/Graphics/GraphicsTools/CMakeLists.txt +++ b/Graphics/GraphicsTools/CMakeLists.txt @@ -4,6 +4,7 @@ project(Diligent-GraphicsTools CXX) set(INTERFACE interface/CommonlyUsedStates.h + interface/DurationQueryHelper.h interface/GraphicsUtilities.h interface/pch.h interface/ScopedQueryHelper.h @@ -14,6 +15,7 @@ set(INTERFACE ) set(SOURCE + src/DurationQueryHelper.cpp src/GraphicsUtilities.cpp src/ScopedQueryHelper.cpp src/ScreenCapture.cpp diff --git a/Graphics/GraphicsTools/interface/DurationQueryHelper.h b/Graphics/GraphicsTools/interface/DurationQueryHelper.h new file mode 100644 index 00000000..38c2325d --- /dev/null +++ b/Graphics/GraphicsTools/interface/DurationQueryHelper.h @@ -0,0 +1,103 @@ +/* + * Copyright 2019-2020 Diligent Graphics LLC + * Copyright 2015-2019 Egor Yusov + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * In no event and under no legal theory, whether in tort (including negligence), + * contract, or otherwise, unless required by applicable law (such as deliberate + * and grossly negligent acts) or agreed to in writing, shall any Contributor be + * liable for any damages, including any direct, indirect, special, incidental, + * or consequential damages of any character arising as a result of this License or + * out of the use or inability to use the software (including but not limited to damages + * for loss of goodwill, work stoppage, computer failure or malfunction, or any and + * all other commercial damages or losses), even if such Contributor has been advised + * of the possibility of such damages. + */ + +#pragma once + +#include +#include + +#include "../../GraphicsEngine/interface/RenderDevice.h" +#include "../../GraphicsEngine/interface/DeviceContext.h" +#include "../../GraphicsEngine/interface/Query.h" +#include "../../../Common/interface/RefCntAutoPtr.h" + +namespace Diligent +{ + +/// Helper class to manage duration queries. +/// One DurationQueryHelper instance must be used once per frame. +class DurationQueryHelper +{ +public: + DurationQueryHelper(IRenderDevice* pDevice, + Uint32 NumQueriesToReserve, + Uint32 ExpectedQueryLimit = 5); + + // clang-format off + DurationQueryHelper (const DurationQueryHelper&) = delete; + DurationQueryHelper& operator=(const DurationQueryHelper&) = delete; + DurationQueryHelper (DurationQueryHelper&&) = default; + DurationQueryHelper& operator=(DurationQueryHelper&&) = default; + // clang-format on + + + /// Begins a query. + + /// \param [in] pCtx - Context to record begin query command + /// + /// \remarks There must be exaclty one matching Begin() for every End() call, otherwise + /// the behavior is undefined. + void Begin(IDeviceContext* pCtx); + + + /// Ends a query and returns the duration, in seconds, if it is available. + + /// \param [in] pCtx - Context to record end query command. + /// \param [out] Duration - Variable where duration will be written to. + /// \return true if the data from the oldest query is available, and false otherwise. + /// + /// \remarks There must be exaclty one matching End() for every Begin() call, otherwise + /// the behavior is undefined. + bool End(IDeviceContext* pCtx, double& Duration); + +private: + RefCntAutoPtr m_pDevice; + + const Uint32 m_ExpectedQueryLimit; + + struct DurationQuery + { + DurationQuery(IRenderDevice* pDevice); + + // clang-format off + DurationQuery (const DurationQuery&) = delete; + DurationQuery& operator=(const DurationQuery&) = delete; + DurationQuery (DurationQuery&&) = default; + DurationQuery& operator=(DurationQuery&&) = default; + // clang-format on + + RefCntAutoPtr StartTimestamp; + RefCntAutoPtr EndTimestamp; + }; + void InitDurationQuery(DurationQuery& query); + + + std::deque m_PendingQueries; + std::vector m_AvailableQueries; +}; + +} // namespace Diligent diff --git a/Graphics/GraphicsTools/src/DurationQueryHelper.cpp b/Graphics/GraphicsTools/src/DurationQueryHelper.cpp new file mode 100644 index 00000000..6f69bd0c --- /dev/null +++ b/Graphics/GraphicsTools/src/DurationQueryHelper.cpp @@ -0,0 +1,114 @@ +/* + * Copyright 2019-2020 Diligent Graphics LLC + * Copyright 2015-2019 Egor Yusov + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * In no event and under no legal theory, whether in tort (including negligence), + * contract, or otherwise, unless required by applicable law (such as deliberate + * and grossly negligent acts) or agreed to in writing, shall any Contributor be + * liable for any damages, including any direct, indirect, special, incidental, + * or consequential damages of any character arising as a result of this License or + * out of the use or inability to use the software (including but not limited to damages + * for loss of goodwill, work stoppage, computer failure or malfunction, or any and + * all other commercial damages or losses), even if such Contributor has been advised + * of the possibility of such damages. + */ + +#include "pch.h" +#include "DurationQueryHelper.h" + +namespace Diligent +{ + +DurationQueryHelper::DurationQuery::DurationQuery(IRenderDevice* pDevice) +{ + QueryDesc queryDesc{QUERY_TYPE_TIMESTAMP}; + + queryDesc.Name = "Duration start timestamp query"; + pDevice->CreateQuery(queryDesc, &StartTimestamp); + VERIFY(StartTimestamp, "Failed to create start query"); + + queryDesc.Name = "Duration end timestamp query"; + pDevice->CreateQuery(queryDesc, &EndTimestamp); + VERIFY(EndTimestamp, "Failed to create end query"); +} + +DurationQueryHelper::DurationQueryHelper(IRenderDevice* pDevice, + Uint32 NumQueriesToReserve, + Uint32 ExpectedQueryLimit) : + m_pDevice{pDevice}, + m_ExpectedQueryLimit{ExpectedQueryLimit} +{ + m_AvailableQueries.reserve(NumQueriesToReserve); + for (Uint32 i = 0; i < NumQueriesToReserve; ++i) + { + m_AvailableQueries.emplace_back(pDevice); + } +} + +void DurationQueryHelper::Begin(IDeviceContext* pCtx) +{ + if (m_AvailableQueries.empty()) + { + m_AvailableQueries.emplace_back(m_pDevice); + } + + auto DurationQuery = std::move(m_AvailableQueries.back()); + m_AvailableQueries.pop_back(); + + pCtx->EndQuery(DurationQuery.StartTimestamp); + m_PendingQueries.push_front(std::move(DurationQuery)); +} + +bool DurationQueryHelper::End(IDeviceContext* pCtx, double& Duration) +{ + if (m_PendingQueries.empty()) + { + LOG_ERROR_MESSAGE("There are no pending queries, which likely indicates incosistent Begin()/End() calls"); + return false; + } + + if (m_PendingQueries.size() > m_ExpectedQueryLimit) + { + LOG_WARNING_MESSAGE("There are ", m_PendingQueries.size(), " pending queries which exceeds the specified expected limit (", m_ExpectedQueryLimit, ")"); + } + + pCtx->EndQuery(m_PendingQueries.front().EndTimestamp); + + auto& LastQuery = m_PendingQueries.back(); + + QueryDataTimestamp StartTimestampData; + + // Do not invalidate the query until we also get end timestamp + auto DataAvailable = LastQuery.StartTimestamp->GetData(&StartTimestampData, sizeof(StartTimestampData), false); + if (DataAvailable) + { + QueryDataTimestamp EndTimestampData; + DataAvailable = LastQuery.EndTimestamp->GetData(&EndTimestampData, sizeof(EndTimestampData)); + if (DataAvailable) + { + LastQuery.StartTimestamp->Invalidate(); + Duration = + static_cast(EndTimestampData.Counter) / static_cast(EndTimestampData.Frequency) - + static_cast(StartTimestampData.Counter) / static_cast(StartTimestampData.Frequency); + } + + m_AvailableQueries.emplace_back(std::move(LastQuery)); + m_PendingQueries.pop_back(); + } + + return DataAvailable; +} + +} // namespace Diligent -- cgit v1.2.3