3
0

DecompressorStackEntry.h 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155
  1. /*
  2. * Copyright (c) Contributors to the Open 3D Engine Project.
  3. * For complete copyright and license terms please see the LICENSE at the root of this distribution.
  4. *
  5. * SPDX-License-Identifier: Apache-2.0 OR MIT
  6. *
  7. */
  8. #pragma once
  9. #include <AzCore/IO/Streamer/Statistics.h>
  10. #include <AzCore/IO/Streamer/StreamerConfiguration.h>
  11. #include <AzCore/IO/Streamer/StreamStackEntry.h>
  12. #include <AzCore/Memory/SystemAllocator.h>
  13. #include <AzCore/std/chrono/chrono.h>
  14. #include <AzCore/std/containers/deque.h>
  15. #include <AzCore/std/smart_ptr/unique_ptr.h>
  16. #include <AzCore/Statistics/RunningStatistic.h>
  17. #include <AzCore/Task/TaskExecutor.h>
  18. #include <AzCore/Task/TaskGraph.h>
  19. namespace AZ::IO
  20. {
  21. class RequestPath;
  22. }
  23. namespace AZ::IO::Requests
  24. {
  25. struct ReadRequestData;
  26. struct ReportData;
  27. }
  28. namespace Compression
  29. {
  30. //! Thunk structure used to create a DecompressorRegistrarEntry instance
  31. //! and add it to the StreamerStack without needing public API access
  32. //! to this DecompressorRegistrarEntry outside of this gem
  33. //! Streamer uses the SerializeContext to load any derived IStreamerStackConfig
  34. //! classes listed under the "/Amazon/AzCore/Streamer/Profiles" keys
  35. //! from the merged Settings registry(include .setreg files)
  36. //! and invokes the virtual AddStreamStackEntry function on it to create the actual instance
  37. struct DecompressorRegistrarConfig final
  38. : public AZ::IO::IStreamerStackConfig
  39. {
  40. AZ_TYPE_INFO_WITH_NAME_DECL(DecompressorRegistrarConfig);
  41. AZ_RTTI_NO_TYPE_INFO_DECL();
  42. AZ_CLASS_ALLOCATOR_DECL;
  43. ~DecompressorRegistrarConfig() override = default;
  44. AZStd::shared_ptr<AZ::IO::StreamStackEntry> AddStreamStackEntry(
  45. const AZ::IO::HardwareInformation& hardware, AZStd::shared_ptr<AZ::IO::StreamStackEntry> parent) override;
  46. static void Reflect(AZ::ReflectContext* context);
  47. //! Maximum number of reads that are kept in flight.
  48. AZ::u32 m_maxNumReads{ 2 };
  49. //! Maximum number of decompression tasks that can run simultaneously.
  50. AZ::u32 m_maxNumTasks{ 2 };
  51. };
  52. //! Decompression Entry in the streamer stack that is used to look up registered compression interfaces
  53. //! The decompression is performed in a temporary buffer on a separate thread using the Task system
  54. //! as single files and without equally distributed seek points.
  55. //! Because the target archive has compressed the entire file, it needs to be decompressed
  56. //! completely, so even if the file is partially read, it needs to be fully loaded. This
  57. //! also means that there's no upper limit to the memory so every decompression job will
  58. //! need to allocate memory as a temporary buffer (in-place decompression is not supported).
  59. //! Finally, the lack of an upper limit also means that the duration of the decompression job
  60. //! can vary largely so a dedicated job system is used to decompress on to avoid blocking
  61. //! the main job system from working.
  62. class DecompressorRegistrarEntry
  63. : public AZ::IO::StreamStackEntry
  64. {
  65. public:
  66. DecompressorRegistrarEntry(AZ::u32 maxNumReads, AZ::u32 maxNumTasks, AZ::u32 alignment);
  67. ~DecompressorRegistrarEntry() override = default;
  68. void PrepareRequest(AZ::IO::FileRequest* request) override;
  69. void QueueRequest(AZ::IO::FileRequest* request) override;
  70. bool ExecuteRequests() override;
  71. void UpdateStatus(Status& status) const override;
  72. void UpdateCompletionEstimates(AZStd::chrono::steady_clock::time_point now, AZStd::vector<AZ::IO::FileRequest*>& internalPending,
  73. AZ::IO::StreamerContext::PreparedQueue::iterator pendingBegin, AZ::IO::StreamerContext::PreparedQueue::iterator pendingEnd) override;
  74. void CollectStatistics(AZStd::vector<AZ::IO::Statistic>& statistics) const override;
  75. private:
  76. using Buffer = AZ::u8*;
  77. enum class ReadBufferStatus : uint8_t
  78. {
  79. Unused,
  80. ReadInFlight,
  81. PendingDecompression
  82. };
  83. struct DecompressionInformation
  84. {
  85. bool IsProcessing() const;
  86. AZStd::chrono::steady_clock::time_point m_queueStartTime;
  87. AZStd::chrono::steady_clock::time_point m_jobStartTime;
  88. Buffer m_compressedData{ nullptr };
  89. AZ::IO::FileRequest* m_waitRequest{ nullptr };
  90. AZ::u32 m_alignmentOffset{ 0 };
  91. };
  92. bool IsIdle() const;
  93. void PrepareReadRequest(AZ::IO::FileRequest* request, AZ::IO::Requests::ReadRequestData& data);
  94. void PrepareDedicatedCache(AZ::IO::FileRequest* request, const AZ::IO::RequestPath& path);
  95. void FileExistsCheck(AZ::IO::FileRequest* checkRequest);
  96. void EstimateCompressedReadRequest(AZ::IO::FileRequest* request, AZStd::chrono::microseconds& cumulativeDelay,
  97. AZStd::chrono::microseconds decompressionDelay, double totalDecompressionDurationUs, double totalBytesDecompressed) const;
  98. void StartArchiveRead(AZ::IO::FileRequest* compressedReadRequest);
  99. void FinishArchiveRead(AZ::IO::FileRequest* readRequest, AZ::u32 readSlot);
  100. bool StartDecompressions();
  101. void FinishDecompression(AZ::IO::FileRequest* waitRequest, AZ::u32 jobSlot);
  102. static void FullDecompression(AZ::IO::StreamerContext* context, DecompressionInformation& info);
  103. static void PartialDecompression(AZ::IO::StreamerContext* context, DecompressionInformation& info);
  104. void Report(const AZ::IO::Requests::ReportData& data) const;
  105. AZStd::deque<AZ::IO::FileRequest*> m_pendingReads;
  106. AZStd::deque<AZ::IO::FileRequest*> m_pendingFileExistChecks;
  107. AZ::IO::AverageWindow<size_t, double, AZ::IO::s_statisticsWindowSize> m_decompressionJobDelayMicroSec;
  108. AZ::IO::AverageWindow<size_t, double, AZ::IO::s_statisticsWindowSize> m_decompressionDurationMicroSec;
  109. AZ::IO::AverageWindow<size_t, double, AZ::IO::s_statisticsWindowSize> m_bytesDecompressed;
  110. #if AZ_STREAMER_ADD_EXTRA_PROFILING_INFO
  111. AZ::Statistics::RunningStatistic m_decompressionBoundStat;
  112. AZ::Statistics::RunningStatistic m_readBoundStat;
  113. #endif
  114. // Task executor for decompressor
  115. AZ::TaskExecutor m_taskExecutor;
  116. AZStd::unique_ptr<AZ::TaskGraphEvent> m_taskGraphEvent;
  117. AZStd::unique_ptr<Buffer[]> m_readBuffers;
  118. // Nullptr if not reading, the read request if reading the file and the wait request for decompression when waiting on decompression.
  119. AZStd::unique_ptr<AZ::IO::FileRequest*[]> m_readRequests;
  120. AZStd::unique_ptr<ReadBufferStatus[]> m_readBufferStatus;
  121. AZStd::unique_ptr<DecompressionInformation[]> m_processingJobs;
  122. size_t m_memoryUsage{ 0 }; //!< Amount of memory used for buffers by the decompressor.
  123. AZ::u32 m_maxNumReads{ 2 };
  124. AZ::u32 m_numInFlightReads{ 0 };
  125. AZ::u32 m_numPendingDecompression{ 0 };
  126. AZ::u32 m_maxNumTasks{ 1 };
  127. AZ::u32 m_numRunningTasks{ 0 };
  128. AZ::u32 m_alignment{ 0 };
  129. };
  130. } // namespace AZ::IO