/* * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or * its licensors. * * For complete copyright and license terms please see the LICENSE at the root of this * distribution (the "License"). All use of this software is governed by the License, * or, if provided, by the license below or the license accompanying this file. Do not * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * */ // Original file Copyright Crytek GMBH or its affiliates, used under license. // Description : Manages geometry cache instances and streaming #ifndef CRYINCLUDE_CRY3DENGINE_GEOMCACHEMANAGER_H #define CRYINCLUDE_CRY3DENGINE_GEOMCACHEMANAGER_H #pragma once #if defined(USE_GEOM_CACHES) #include "GeomCacheDecoder.h" #include "GeomCacheMeshManager.h" #include #include class CGeomCache; class CGeomCacheRenderNode; struct SGeomCacheStreamInfo; struct SGeomCacheBufferHandle { SGeomCacheBufferHandle() { m_pNext = NULL; m_startFrame = 0; m_endFrame = 0; m_bufferSize = 0; m_pBuffer = NULL; m_pStream = NULL; m_numJobReferences = 0; } volatile int m_numJobReferences; uint32 m_bufferSize; uint32 m_startFrame; uint32 m_endFrame; char* m_pBuffer; SGeomCacheStreamInfo* m_pStream; CTimeValue m_frameTime; // Next buffer handle for this stream or in the free list SGeomCacheBufferHandle* m_pNext; CryConditionVariable m_jobReferencesCV; }; // This handle represents a block in the read buffer struct SGeomCacheReadRequestHandle : public SGeomCacheBufferHandle , public IStreamCallback { // IStreamCallback virtual void StreamOnComplete(IReadStream* pStream, unsigned nError) {} virtual void StreamAsyncOnComplete(IReadStream* pStream, unsigned nError) { if (nError != 0 && nError != ERROR_USER_ABORT) { string error = "Geom cache read request failed with error: " + string(pStream->GetErrorName()); gEnv->pLog->LogError("%s", error.c_str()); } m_state = eRRHS_FinishedRead; m_error = nError; if (CryInterlockedDecrement(&m_numJobReferences) == 0) { m_jobReferencesCV.Notify(); } } enum EReadRequestHandleState { eRRHS_Reading = 0, eRRHS_FinishedRead = 1, eRRHS_Decompressing = 2, eRRHS_Done = 3 }; volatile EReadRequestHandleState m_state; volatile long m_error; IReadStreamPtr m_pReadStream; }; struct SGeomCacheStreamInfo { SGeomCacheStreamInfo(CGeomCacheRenderNode* pRenderNode, CGeomCache* pGeomCache, const uint numFrames) : m_pRenderNode(pRenderNode) , m_pGeomCache(pGeomCache) , m_numFrames(numFrames) , m_displayedFrameTime(-1.0f) , m_wantedPlaybackTime(0.0f) , m_wantedFloorFrame(0) , m_wantedCeilFrame(0) , m_sameFrameFillCount(0) , m_numFramesMissed(0) , m_pOldestReadRequestHandle(NULL) , m_pNewestReadRequestHandle(NULL) , m_pReadAbortListHead(NULL) , m_pOldestDecompressHandle(NULL) , m_pNewestDecompressHandle(NULL) , m_pDecompressAbortListHead(NULL) , m_bAbort(0L) , m_bLooping(false) {} CGeomCacheRenderNode* m_pRenderNode; CGeomCache* m_pGeomCache; uint m_numFrames; volatile float m_displayedFrameTime; volatile float m_wantedPlaybackTime; volatile uint m_wantedFloorFrame; volatile uint m_wantedCeilFrame; volatile int m_sameFrameFillCount; uint m_numFramesMissed; SGeomCacheReadRequestHandle* m_pOldestReadRequestHandle; SGeomCacheReadRequestHandle* m_pNewestReadRequestHandle; SGeomCacheReadRequestHandle* m_pReadAbortListHead; SGeomCacheBufferHandle* m_pOldestDecompressHandle; SGeomCacheBufferHandle* m_pNewestDecompressHandle; SGeomCacheBufferHandle* m_pDecompressAbortListHead; volatile bool m_bAbort; CryCriticalSection m_abortCS; bool m_bLooping; AZ::LegacyJobExecutor m_fillRenderNodeJobExecutor; struct SFrameData { bool m_bDecompressJobLaunched; // For each frame we initialize a counter to the number of jobs // that need to complete before the frame can be decoded. // // Each index frame has exactly one dependent job (inflate) // The first B frame after an index frame has three dependencies (inflate + previous and last index frame) // All other B frames have only two dependencies (inflate + previous B frame) int m_decodeDependencyCounter; // Pointer to decompress handle for this frame. SGeomCacheBufferHandle* m_pDecompressHandle; }; // Array with data for each frame std::vector m_frameData; }; struct SDecodeFrameJobData { uint m_frameIndex; const CGeomCache* m_pGeomCache; SGeomCacheStreamInfo* m_pStreamInfo; }; class CGeomCacheManager : public Cry3DEngineBase , public AzFramework::LegacyAssetEventBus::Handler { public: CGeomCacheManager(); ~CGeomCacheManager(); // Called during level unload to free all resource references void Reset(); CGeomCache* LoadGeomCache(const char* szFileName); void DeleteGeomCache(CGeomCache* pGeomCache); void StreamingUpdate(); void RegisterForStreaming(CGeomCacheRenderNode* pRenderNode); void UnRegisterForStreaming(CGeomCacheRenderNode* pRenderNode, bool bWaitForJobs); float GetPrecachedTime(const IGeomCacheRenderNode* pRenderNode); #ifndef _RELEASE void DrawDebugInfo(); void ResetDebugInfo() { m_numMissedFrames = 0; m_numStreamAborts = 0; m_numFailedAllocs = 0; } #endif void DecompressFrame_JobEntry(SGeomCacheStreamInfo* pStreamInfo, const uint blockIndex, SGeomCacheBufferHandle* pDecompressHandle, SGeomCacheReadRequestHandle* pReadRequestHandle); void FillRenderNodeAsync_JobEntry(SGeomCacheStreamInfo* pStreamInfo); void DecodeIFrame_JobEntry(SDecodeFrameJobData jobState); void DecodeBFrame_JobEntry(SDecodeFrameJobData jobState); CGeomCacheMeshManager& GetMeshManager() { return m_meshManager; } void StopCacheStreamsAndWait(CGeomCache* pGeomCache); CGeomCache* FindGeomCacheByFilename(const char* filename); // For changing the buffer size on runtime. This will do a blocking wait on all active streams, // so it should only be called when we are sure that no caches are playing (e.g. on level load) void ChangeBufferSize(const uint newSizeInMiB); private: // override from LegacyAssetEventBus::Handler void OnFileChanged(AZStd::string assetPath) override; static void OnChangeBufferSize(ICVar* pCVar); void ReinitializeStreamFrameData(SGeomCacheStreamInfo& streamInfo, uint startFrame, uint endFrame); void UnloadGeomCaches(); bool IssueDiskReadRequest(SGeomCacheStreamInfo& pStreamInfo); void LaunchStreamingJobs(const uint numStreams, const CTimeValue currentFrameTime); void LaunchDecompressJobs(SGeomCacheStreamInfo* pStreamInfo, const CTimeValue currentFrameTime); void LaunchDecodeJob(SDecodeFrameJobData jobState); template TBufferHandleType* NewBufferHandle(const uint32 size, SGeomCacheStreamInfo& streamInfo); SGeomCacheReadRequestHandle* NewReadRequestHandle(const uint32 size, SGeomCacheStreamInfo& streamInfo); void RetireHandles(SGeomCacheStreamInfo& streamInfo); void RetireOldestReadRequestHandle(SGeomCacheStreamInfo& streamInfo); void RetireOldestDecompressHandle(SGeomCacheStreamInfo& streamInfo); void RetireDecompressHandle(SGeomCacheStreamInfo& streamInfo, SGeomCacheBufferHandle* pHandle); template void RetireBufferHandle(TBufferHandleType* pHandle); void RetireRemovedStreams(); void ValidateStream(SGeomCacheStreamInfo& streamInfo); void AbortStream(SGeomCacheStreamInfo& streamInfo); void AbortStreamAndWait(SGeomCacheStreamInfo& streamInfo); void RetireAbortedHandles(SGeomCacheStreamInfo& streamInfo); SGeomCacheBufferHandle* GetFrameDecompressHandle(SGeomCacheStreamInfo* pStreamInfo, const uint frameIndex); SGeomCacheFrameHeader* GetFrameDecompressHeader(SGeomCacheStreamInfo* pStreamInfo, const uint frameIndex); char* GetFrameDecompressData(SGeomCacheStreamInfo* pStreamInfo, const uint frameIndex); int* GetDependencyCounter(SGeomCacheStreamInfo* pStreamInfo, const uint frameIndex); void* m_pPoolBaseAddress; IGeneralMemoryHeap* m_pPool; size_t m_poolSize; uint m_lastRequestStream; uint m_numMissedFrames; uint m_numStreamAborts; uint m_numErrorAborts; uint m_numDecompressStreamAborts; uint m_numReadStreamAborts; uint m_numFailedAllocs; typedef std::vector::iterator TStreamInfosIter; std::vector m_streamInfos; std::vector m_streamInfosAbortList; typedef std::map > TGeomCacheMap; TGeomCacheMap m_nameToGeomCacheMap; CGeomCacheMeshManager m_meshManager; }; #endif #endif // CRYINCLUDE_CRY3DENGINE_GEOMCACHEMANAGER_H