summaryrefslogtreecommitdiffstats
path: root/src/runtimerender/resourcemanager/Qt3DSRenderImageBatchLoader.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/runtimerender/resourcemanager/Qt3DSRenderImageBatchLoader.cpp')
-rw-r--r--src/runtimerender/resourcemanager/Qt3DSRenderImageBatchLoader.cpp538
1 files changed, 538 insertions, 0 deletions
diff --git a/src/runtimerender/resourcemanager/Qt3DSRenderImageBatchLoader.cpp b/src/runtimerender/resourcemanager/Qt3DSRenderImageBatchLoader.cpp
new file mode 100644
index 0000000..72495cd
--- /dev/null
+++ b/src/runtimerender/resourcemanager/Qt3DSRenderImageBatchLoader.cpp
@@ -0,0 +1,538 @@
+/****************************************************************************
+**
+** Copyright (C) 2008-2012 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#include "Qt3DSRenderImageBatchLoader.h"
+#include "foundation/Qt3DSMutex.h"
+#include "foundation/Qt3DSSync.h"
+#include "foundation/Qt3DSContainers.h"
+#include "foundation/Qt3DSAtomic.h"
+#include "foundation/Qt3DSBroadcastingAllocator.h"
+#include "foundation/Qt3DSFoundation.h"
+#include "foundation/StringTable.h"
+#include "Qt3DSRenderInputStreamFactory.h"
+#include "Qt3DSRenderBufferManager.h"
+#include "Qt3DSRenderThreadPool.h"
+#include "Qt3DSRenderImageScaler.h"
+#include "Qt3DSRenderLoadedTexture.h"
+#include "foundation/Qt3DSInvasiveLinkedList.h"
+#include "foundation/Qt3DSPool.h"
+#include "foundation/Qt3DSPerfTimer.h"
+
+using namespace qt3ds::render;
+
+namespace {
+
+struct SImageLoaderBatch;
+typedef Mutex::ScopedLock TScopedLock;
+
+struct SLoadingImage
+{
+ SImageLoaderBatch *m_Batch;
+ CRegisteredString m_SourcePath;
+ QT3DSU64 m_TaskId;
+ SLoadingImage *m_Tail;
+
+ // Called from main thread
+ SLoadingImage(CRegisteredString inSourcePath)
+ : m_Batch(NULL)
+ , m_SourcePath(inSourcePath)
+ , m_TaskId(0)
+ , m_Tail(NULL)
+ {
+ }
+ SLoadingImage()
+ : m_Batch(NULL)
+ , m_TaskId(0)
+ , m_Tail(NULL)
+ {
+ }
+ // Called from main thread
+ void Setup(SImageLoaderBatch &inBatch);
+
+ // Called from loader thread
+ static void LoadImage(void *inImg);
+
+ // Potentially called from loader thread
+ static void TaskCancelled(void *inImg);
+};
+
+struct SLoadingImageTailOp
+{
+ SLoadingImage *get(SLoadingImage &inImg) { return inImg.m_Tail; }
+ void set(SLoadingImage &inImg, SLoadingImage *inItem) { inImg.m_Tail = inItem; }
+};
+
+typedef InvasiveSingleLinkedList<SLoadingImage, SLoadingImageTailOp> TLoadingImageList;
+
+struct SBatchLoader;
+
+struct SImageLoaderBatch
+{
+ // All variables setup in main thread and constant from then on except
+ // loaded image count.
+ SBatchLoader &m_Loader;
+ NVScopedRefCounted<IImageLoadListener> m_LoadListener;
+ Sync m_LoadEvent;
+ Mutex m_LoadMutex;
+ TLoadingImageList m_Images;
+
+ TImageBatchId m_BatchId;
+ // Incremented in main thread
+ QT3DSU32 m_LoadedOrCanceledImageCount;
+ QT3DSU32 m_FinalizedImageCount;
+ QT3DSU32 m_NumImages;
+ NVRenderContextType m_contextType;
+ bool m_preferKTX;
+ bool m_ibl;
+
+ // Called from main thread
+ static SImageLoaderBatch *CreateLoaderBatch(SBatchLoader &inLoader, TImageBatchId inBatchId,
+ NVConstDataRef<CRegisteredString> inSourcePaths,
+ CRegisteredString inImageTillLoaded,
+ IImageLoadListener *inListener,
+ NVRenderContextType contextType,
+ bool preferKTX, bool ibl);
+
+ // Called from main thread
+ SImageLoaderBatch(SBatchLoader &inLoader, IImageLoadListener *inLoadListener,
+ const TLoadingImageList &inImageList, TImageBatchId inBatchId,
+ QT3DSU32 inImageCount, NVRenderContextType contextType,
+ bool preferKTX, bool ibl);
+
+ // Called from main thread
+ ~SImageLoaderBatch();
+
+ // Called from main thread
+ bool IsLoadingFinished()
+ {
+ Mutex::ScopedLock __locker(m_LoadMutex);
+ return m_LoadedOrCanceledImageCount >= m_NumImages;
+ }
+
+ bool IsFinalizedFinished()
+ {
+ Mutex::ScopedLock __locker(m_LoadMutex);
+ return m_FinalizedImageCount >= m_NumImages;
+ }
+
+ void IncrementLoadedImageCount()
+ {
+ Mutex::ScopedLock __locker(m_LoadMutex);
+ ++m_LoadedOrCanceledImageCount;
+ }
+ void IncrementFinalizedImageCount()
+ {
+ Mutex::ScopedLock __locker(m_LoadMutex);
+ ++m_FinalizedImageCount;
+ }
+ // Called from main thread
+ void Cancel();
+ void Cancel(CRegisteredString inSourcePath);
+};
+
+struct SBatchLoadedImage
+{
+ CRegisteredString m_SourcePath;
+ SLoadedTexture *m_Texture;
+ SImageLoaderBatch *m_Batch;
+ SBatchLoadedImage()
+ : m_Texture(NULL)
+ , m_Batch(NULL)
+ {
+ }
+
+ // Called from loading thread
+ SBatchLoadedImage(CRegisteredString inSourcePath, SLoadedTexture *inTexture,
+ SImageLoaderBatch &inBatch)
+ : m_SourcePath(inSourcePath)
+ , m_Texture(inTexture)
+ , m_Batch(&inBatch)
+ {
+ }
+
+ // Called from main thread
+ bool Finalize(IBufferManager &inMgr);
+};
+
+struct SBatchLoader : public IImageBatchLoader
+{
+ typedef nvhash_map<TImageBatchId, SImageLoaderBatch *> TImageLoaderBatchMap;
+ typedef nvhash_map<CRegisteredString, TImageBatchId> TSourcePathToBatchMap;
+ typedef Pool<SLoadingImage, ForwardingAllocator> TLoadingImagePool;
+ typedef Pool<SImageLoaderBatch, ForwardingAllocator> TBatchPool;
+
+ // Accessed from loader thread
+ NVFoundationBase &m_Foundation;
+ volatile QT3DSI32 mRefCount;
+ // Accessed from loader thread
+ IInputStreamFactory &m_InputStreamFactory;
+ //!!Not threadsafe! accessed only from main thread
+ IBufferManager &m_BufferManager;
+ // Accessed from main thread
+ IThreadPool &m_ThreadPool;
+ // Accessed from both threads
+ IPerfTimer &m_PerfTimer;
+ // main thread
+ TImageBatchId m_NextBatchId;
+ // main thread
+ TImageLoaderBatchMap m_Batches;
+ // main thread
+ Mutex m_LoaderMutex;
+
+ // Both loader and main threads
+ nvvector<SBatchLoadedImage> m_LoadedImages;
+ // main thread
+ nvvector<TImageBatchId> m_FinishedBatches;
+ // main thread
+ TSourcePathToBatchMap m_SourcePathToBatches;
+ // main thread
+ nvvector<SLoadingImage> m_LoaderBuilderWorkspace;
+ TLoadingImagePool m_LoadingImagePool;
+ TBatchPool m_BatchPool;
+
+ SBatchLoader(NVFoundationBase &inFoundation, IInputStreamFactory &inFactory,
+ IBufferManager &inBufferManager, IThreadPool &inThreadPool, IPerfTimer &inTimer)
+ : m_Foundation(inFoundation)
+ , mRefCount(0)
+ , m_InputStreamFactory(inFactory)
+ , m_BufferManager(inBufferManager)
+ , m_ThreadPool(inThreadPool)
+ , m_PerfTimer(inTimer)
+ , m_NextBatchId(1)
+ , m_Batches(inFoundation.getAllocator(), "SBatchLoader::m_Batches")
+ , m_LoaderMutex(inFoundation.getAllocator())
+ , m_LoadedImages(inFoundation.getAllocator(), "SBatchLoader::m_LoadedImages")
+ , m_FinishedBatches(inFoundation.getAllocator(), "SBatchLoader::m_FinishedBatches")
+ , m_SourcePathToBatches(inFoundation.getAllocator(), "SBatchLoader::m_SourcePathToBatches")
+ , m_LoaderBuilderWorkspace(inFoundation.getAllocator(),
+ "SBatchLoader::m_LoaderBuilderWorkspace")
+ , m_LoadingImagePool(
+ ForwardingAllocator(inFoundation.getAllocator(), "SBatchLoader::m_LoadingImagePool"))
+ , m_BatchPool(ForwardingAllocator(inFoundation.getAllocator(), "SBatchLoader::m_BatchPool"))
+ {
+ }
+
+ virtual ~SBatchLoader()
+ {
+ nvvector<TImageBatchId> theCancelledBatches(m_Foundation.getAllocator(), "~SBatchLoader");
+ for (TImageLoaderBatchMap::iterator theIter = m_Batches.begin(), theEnd = m_Batches.end();
+ theIter != theEnd; ++theIter) {
+ theIter->second->Cancel();
+ theCancelledBatches.push_back(theIter->second->m_BatchId);
+ }
+ for (QT3DSU32 idx = 0, end = theCancelledBatches.size(); idx < end; ++idx)
+ BlockUntilLoaded(theCancelledBatches[idx]);
+
+ QT3DS_ASSERT(m_Batches.size() == 0);
+ }
+
+ QT3DS_IMPLEMENT_REF_COUNT_ADDREF_RELEASE(m_Foundation.getAllocator())
+
+ // Returns an ID to the load request. Request a block of images to be loaded.
+ // Also takes an image that the buffer system will return when requested for the given source
+ // paths
+ // until said path is loaded.
+ // An optional listener can be passed in to get callbacks about the batch.
+ TImageBatchId LoadImageBatch(NVConstDataRef<CRegisteredString> inSourcePaths,
+ CRegisteredString inImageTillLoaded,
+ IImageLoadListener *inListener,
+ NVRenderContextType contextType,
+ bool preferKTX, bool iblImages) override
+ {
+ if (inSourcePaths.size() == 0)
+ return 0;
+
+ TScopedLock __loaderLock(m_LoaderMutex);
+
+ TImageBatchId theBatchId = 0;
+
+ // Empty loop intentional to find an unused batch id.
+ for (theBatchId = m_NextBatchId; m_Batches.find(theBatchId) != m_Batches.end();
+ ++m_NextBatchId, theBatchId = m_NextBatchId) {
+ }
+
+ SImageLoaderBatch *theBatch(SImageLoaderBatch::CreateLoaderBatch(
+ *this, theBatchId, inSourcePaths, inImageTillLoaded, inListener, contextType,
+ preferKTX, iblImages));
+ if (theBatch) {
+ m_Batches.insert(eastl::make_pair(theBatchId, theBatch));
+ return theBatchId;
+ }
+ return 0;
+ }
+
+ void CancelImageBatchLoading(TImageBatchId inBatchId) override
+ {
+ SImageLoaderBatch *theBatch(GetBatch(inBatchId));
+ if (theBatch)
+ theBatch->Cancel();
+ }
+
+ // Blocks if the image is currently in-flight
+ void CancelImageLoading(CRegisteredString inSourcePath) override
+ {
+ TScopedLock __loaderLock(m_LoaderMutex);
+ TSourcePathToBatchMap::iterator theIter = m_SourcePathToBatches.find(inSourcePath);
+ if (theIter != m_SourcePathToBatches.end()) {
+ TImageBatchId theBatchId = theIter->second;
+ TImageLoaderBatchMap::iterator theBatchIter = m_Batches.find(theBatchId);
+ if (theBatchIter != m_Batches.end())
+ theBatchIter->second->Cancel(inSourcePath);
+ }
+ }
+
+ SImageLoaderBatch *GetBatch(TImageBatchId inId)
+ {
+ TScopedLock __loaderLock(m_LoaderMutex);
+ TImageLoaderBatchMap::iterator theIter = m_Batches.find(inId);
+ if (theIter != m_Batches.end())
+ return theIter->second;
+ return NULL;
+ }
+
+ void BlockUntilLoaded(TImageBatchId inId) override
+ {
+ for (SImageLoaderBatch *theBatch = GetBatch(inId); theBatch; theBatch = GetBatch(inId)) {
+ // Only need to block if images aren't loaded. Don't need to block if they aren't
+ // finalized.
+ if (!theBatch->IsLoadingFinished()) {
+ theBatch->m_LoadEvent.wait(200);
+ theBatch->m_LoadEvent.reset();
+ }
+ BeginFrame(true);
+ }
+ }
+ void ImageLoaded(SLoadingImage &inImage, SLoadedTexture *inTexture)
+ {
+ TScopedLock __loaderLock(m_LoaderMutex);
+ m_LoadedImages.push_back(
+ SBatchLoadedImage(inImage.m_SourcePath, inTexture, *inImage.m_Batch));
+ inImage.m_Batch->IncrementLoadedImageCount();
+ inImage.m_Batch->m_LoadEvent.set();
+ }
+ // These are called by the render context, users don't need to call this.
+ void BeginFrame(bool firstFrame) override
+ {
+ TScopedLock __loaderLock(m_LoaderMutex);
+ // Pass 1 - send out all image loaded signals
+ for (QT3DSU32 idx = 0, end = m_LoadedImages.size(); idx < end; ++idx) {
+
+ m_SourcePathToBatches.erase(m_LoadedImages[idx].m_SourcePath);
+ m_LoadedImages[idx].Finalize(m_BufferManager);
+ m_LoadedImages[idx].m_Batch->IncrementFinalizedImageCount();
+ if (m_LoadedImages[idx].m_Batch->IsFinalizedFinished())
+ m_FinishedBatches.push_back(m_LoadedImages[idx].m_Batch->m_BatchId);
+ if (!firstFrame)
+ break;
+ }
+ if (firstFrame)
+ m_LoadedImages.clear();
+ else if (m_LoadedImages.size())
+ m_LoadedImages.erase(m_LoadedImages.begin());
+ // pass 2 - clean up any existing batches.
+ for (QT3DSU32 idx = 0, end = m_FinishedBatches.size(); idx < end; ++idx) {
+ TImageLoaderBatchMap::iterator theIter = m_Batches.find(m_FinishedBatches[idx]);
+ if (theIter != m_Batches.end()) {
+ SImageLoaderBatch *theBatch = theIter->second;
+ if (theBatch->m_LoadListener)
+ theBatch->m_LoadListener->OnImageBatchComplete(theBatch->m_BatchId);
+ m_Batches.erase(m_FinishedBatches[idx]);
+ theBatch->~SImageLoaderBatch();
+ m_BatchPool.deallocate(theBatch);
+ }
+ }
+ m_FinishedBatches.clear();
+ }
+
+ void EndFrame() override {}
+};
+
+void SLoadingImage::Setup(SImageLoaderBatch &inBatch)
+{
+ m_Batch = &inBatch;
+ m_TaskId = inBatch.m_Loader.m_ThreadPool.AddTask(this, LoadImage, TaskCancelled);
+}
+
+void SLoadingImage::LoadImage(void *inImg)
+{
+ SLoadingImage *theThis = reinterpret_cast<SLoadingImage *>(inImg);
+ SStackPerfTimer theTimer(theThis->m_Batch->m_Loader.m_PerfTimer, "Image Decompression");
+ if (theThis->m_Batch->m_Loader.m_BufferManager.IsImageLoaded(theThis->m_SourcePath) == false) {
+ SLoadedTexture *theTexture = SLoadedTexture::Load(
+ theThis->m_SourcePath.c_str(), theThis->m_Batch->m_Loader.m_Foundation,
+ theThis->m_Batch->m_Loader.m_InputStreamFactory, true,
+ theThis->m_Batch->m_contextType,
+ theThis->m_Batch->m_preferKTX);
+ // if ( theTexture )
+ // theTexture->EnsureMultiplerOfFour( theThis->m_Batch->m_Loader.m_Foundation,
+ //theThis->m_SourcePath.c_str() );
+
+ theThis->m_Batch->m_Loader.ImageLoaded(*theThis, theTexture);
+ } else {
+ theThis->m_Batch->m_Loader.ImageLoaded(*theThis, NULL);
+ }
+}
+
+void SLoadingImage::TaskCancelled(void *inImg)
+{
+ SLoadingImage *theThis = reinterpret_cast<SLoadingImage *>(inImg);
+ theThis->m_Batch->m_Loader.ImageLoaded(*theThis, NULL);
+}
+
+bool SBatchLoadedImage::Finalize(IBufferManager &inMgr)
+{
+ if (m_Texture) {
+ eastl::string thepath(m_SourcePath);
+ bool isIBL = this->m_Batch->m_ibl;
+ inMgr.LoadRenderImage(m_SourcePath, *m_Texture, false, isIBL);
+ inMgr.UnaliasImagePath(m_SourcePath);
+ }
+ if (m_Batch->m_LoadListener)
+ m_Batch->m_LoadListener->OnImageLoadComplete(
+ m_SourcePath, m_Texture ? ImageLoadResult::Succeeded : ImageLoadResult::Failed);
+
+ if (m_Texture) {
+ m_Texture->release();
+ return true;
+ }
+
+ return false;
+}
+
+SImageLoaderBatch *
+SImageLoaderBatch::CreateLoaderBatch(SBatchLoader &inLoader, TImageBatchId inBatchId,
+ NVConstDataRef<CRegisteredString> inSourcePaths,
+ CRegisteredString inImageTillLoaded,
+ IImageLoadListener *inListener,
+ NVRenderContextType contextType,
+ bool preferKTX, bool iblImages)
+{
+ TLoadingImageList theImages;
+ QT3DSU32 theLoadingImageCount = 0;
+ for (QT3DSU32 idx = 0, end = inSourcePaths.size(); idx < end; ++idx) {
+ CRegisteredString theSourcePath(inSourcePaths[idx]);
+
+ if (theSourcePath.IsValid() == false)
+ continue;
+
+ if (inLoader.m_BufferManager.IsImageLoaded(theSourcePath))
+ continue;
+
+ eastl::pair<SBatchLoader::TSourcePathToBatchMap::iterator, bool> theInserter =
+ inLoader.m_SourcePathToBatches.insert(eastl::make_pair(inSourcePaths[idx], inBatchId));
+
+ // If the loader has already seen this image.
+ if (theInserter.second == false)
+ continue;
+
+ if (inImageTillLoaded.IsValid()) {
+ // Alias the image so any further requests for this source path will result in
+ // the default images (image till loaded).
+ bool aliasSuccess =
+ inLoader.m_BufferManager.AliasImagePath(theSourcePath, inImageTillLoaded, true);
+ (void)aliasSuccess;
+ QT3DS_ASSERT(aliasSuccess);
+ }
+
+ theImages.push_front(
+ *inLoader.m_LoadingImagePool.construct(theSourcePath, __FILE__, __LINE__));
+ ++theLoadingImageCount;
+ }
+ if (theImages.empty() == false) {
+ SImageLoaderBatch *theBatch =
+ (SImageLoaderBatch *)inLoader.m_BatchPool.allocate(__FILE__, __LINE__);
+ new (theBatch)
+ SImageLoaderBatch(inLoader, inListener, theImages, inBatchId, theLoadingImageCount,
+ contextType, preferKTX, iblImages);
+ return theBatch;
+ }
+ return NULL;
+}
+
+SImageLoaderBatch::SImageLoaderBatch(SBatchLoader &inLoader, IImageLoadListener *inLoadListener,
+ const TLoadingImageList &inImageList, TImageBatchId inBatchId,
+ QT3DSU32 inImageCount, NVRenderContextType contextType,
+ bool preferKTX, bool ibl)
+ : m_Loader(inLoader)
+ , m_LoadListener(inLoadListener)
+ , m_LoadEvent(inLoader.m_Foundation.getAllocator())
+ , m_LoadMutex(inLoader.m_Foundation.getAllocator())
+ , m_Images(inImageList)
+ , m_BatchId(inBatchId)
+ , m_LoadedOrCanceledImageCount(0)
+ , m_FinalizedImageCount(0)
+ , m_NumImages(inImageCount)
+ , m_contextType(contextType)
+ , m_preferKTX(preferKTX)
+ , m_ibl(ibl)
+{
+ for (TLoadingImageList::iterator iter = m_Images.begin(), end = m_Images.end(); iter != end;
+ ++iter) {
+ iter->Setup(*this);
+ }
+}
+
+SImageLoaderBatch::~SImageLoaderBatch()
+{
+ for (TLoadingImageList::iterator iter = m_Images.begin(), end = m_Images.end(); iter != end;
+ ++iter) {
+ TLoadingImageList::iterator temp(iter);
+ ++iter;
+ m_Loader.m_LoadingImagePool.deallocate(temp.m_Obj);
+ }
+}
+
+void SImageLoaderBatch::Cancel()
+{
+ for (TLoadingImageList::iterator iter = m_Images.begin(), end = m_Images.end(); iter != end;
+ ++iter)
+ m_Loader.m_ThreadPool.CancelTask(iter->m_TaskId);
+}
+
+void SImageLoaderBatch::Cancel(CRegisteredString inSourcePath)
+{
+ for (TLoadingImageList::iterator iter = m_Images.begin(), end = m_Images.end(); iter != end;
+ ++iter) {
+ if (iter->m_SourcePath == inSourcePath) {
+ m_Loader.m_ThreadPool.CancelTask(iter->m_TaskId);
+ break;
+ }
+ }
+}
+}
+
+IImageBatchLoader &IImageBatchLoader::CreateBatchLoader(NVFoundationBase &inFoundation,
+ IInputStreamFactory &inFactory,
+ IBufferManager &inBufferManager,
+ IThreadPool &inThreadPool,
+ IPerfTimer &inTimer)
+{
+ return *QT3DS_NEW(inFoundation.getAllocator(),
+ SBatchLoader)(inFoundation, inFactory, inBufferManager, inThreadPool, inTimer);
+}