diff --git a/src/util/meson.build b/src/util/meson.build index 55f4482a..b064fee7 100644 --- a/src/util/meson.build +++ b/src/util/meson.build @@ -2,6 +2,7 @@ util_src = files([ 'util_env.cpp', 'util_string.cpp', 'util_fps_limiter.cpp', + 'util_flush.cpp', 'util_gdi.cpp', 'util_luid.cpp', 'util_matrix.cpp', diff --git a/src/util/util_flush.cpp b/src/util/util_flush.cpp new file mode 100644 index 00000000..a4d91bd4 --- /dev/null +++ b/src/util/util_flush.cpp @@ -0,0 +1,79 @@ +#include "util_flush.h" + +namespace dxvk { + + bool GpuFlushTracker::considerFlush( + GpuFlushType flushType, + uint64_t chunkId, + uint32_t lastCompleteSubmissionId) { + constexpr uint32_t minPendingSubmissions = 2; + + constexpr uint32_t minChunkCount = 3u; + constexpr uint32_t maxChunkCount = 20u; + + // Do not flush if there is nothing to flush + uint32_t chunkCount = uint32_t(chunkId - m_lastFlushChunkId); + + if (!chunkCount) + return false; + + // Take any earlier missed flush with a stronger hint into account, so + // that we still flush those as soon as possible. Ignore synchronization + // commands since they will either perform a flush or not need it at all. + flushType = std::min(flushType, m_lastMissedType); + + if (flushType != GpuFlushType::ImplicitSynchronization) + m_lastMissedType = flushType; + + switch (flushType) { + case GpuFlushType::ExplicitFlush: { + // This shouldn't really be called for explicit flushes, + // but handle them anyway for the sake of completeness + return true; + } + + case GpuFlushType::ImplicitStrongHint: { + // Flush aggressively with a strong hint to reduce readback latency. + return chunkCount >= minChunkCount; + } + + case GpuFlushType::ImplicitWeakHint: { + // Aim for a higher number of chunks per submission with + // a weak hint in order to avoid submitting too often. + if (chunkCount < 2 * minChunkCount) + return false; + + // Actual heuristic is shared with synchronization commands + } [[fallthrough]]; + + case GpuFlushType::ImplicitSynchronization: { + // If the GPU is about to go idle, flush aggressively. This may be + // required if the application is spinning on a query or resource. + uint32_t pendingSubmissions = uint32_t(m_lastFlushSubmissionId - lastCompleteSubmissionId); + + if (pendingSubmissions < minPendingSubmissions) + return true; + + // Use the number of pending submissions to decide whether to flush. Other + // than ignoring the minimum chunk count condition, we should treat this + // the same as weak hints to avoid unnecessary synchronization. + uint32_t threshold = std::min(maxChunkCount, pendingSubmissions * minChunkCount); + return chunkCount >= threshold; + } + } + + // Should be unreachable + return false; + } + + + void GpuFlushTracker::notifyFlush( + uint64_t chunkId, + uint64_t submissionId) { + m_lastMissedType = GpuFlushType::ImplicitWeakHint; + + m_lastFlushChunkId = chunkId; + m_lastFlushSubmissionId = submissionId; + } + +} diff --git a/src/util/util_flush.h b/src/util/util_flush.h new file mode 100644 index 00000000..138aa992 --- /dev/null +++ b/src/util/util_flush.h @@ -0,0 +1,71 @@ +#pragma once + +#include +#include +#include + +namespace dxvk { + + /** + * \brief GPU context flush type + */ + enum class GpuFlushType : uint32_t { + /** Flush or Present called by application */ + ExplicitFlush = 0, + /** Function that requires GPU synchronization and + * may require a flush called by application */ + ImplicitSynchronization = 1, + /** GPU command that applications are likely to synchronize + * with soon has been recorded into the command list */ + ImplicitStrongHint = 2, + /** GPU commands have been recorded and a flush should be + * performed if the current command list is large enough. */ + ImplicitWeakHint = 3, + }; + + + /** + * \brief GPU flush tracker + * + * Helper class that implements a context flush + * heuristic for various scenarios. + */ + class GpuFlushTracker { + + public: + + /** + * \brief Checks whether a context flush should be performed + * + * Note that this modifies internal state, and depending on the + * flush type, this may influence the decision for future flushes. + * \param [in] flushType Flush type + * \param [in] chunkId GPU command sequence number + * \param [in] lastCompleteSubmissionId Last completed command submission ID + * \returns \c true if a flush should be performed + */ + bool considerFlush( + GpuFlushType flushType, + uint64_t chunkId, + uint32_t lastCompleteSubmissionId); + + /** + * \brief Notifies tracker about a context flush + * + * \param [in] chunkId GPU command sequence number + * \param [in] submissionId Command submission ID + */ + void notifyFlush( + uint64_t chunkId, + uint64_t submissionId); + + private: + + GpuFlushType m_lastMissedType = GpuFlushType::ImplicitWeakHint; + + uint64_t m_lastFlushChunkId = 0ull; + uint64_t m_lastFlushSubmissionId = 0ull; + + }; + +}