diff options
13 files changed, 336 insertions, 151 deletions
diff --git a/src/engine/Qt3DSRenderRuntimeBinding.cpp b/src/engine/Qt3DSRenderRuntimeBinding.cpp index 2a6cbde..50ef3df 100644 --- a/src/engine/Qt3DSRenderRuntimeBinding.cpp +++ b/src/engine/Qt3DSRenderRuntimeBinding.cpp @@ -703,7 +703,7 @@ struct Qt3DSRenderScene : public Q3DStudio::IScene nvvector<SGraphObject *>::iterator end = vec.end(); while (it != end) { if ((*it)->m_Type == type) - callable(static_cast<T*>(*it)); + callable(static_cast<T*>(*it), preferKtx()); ++it; } } @@ -711,12 +711,14 @@ struct Qt3DSRenderScene : public Q3DStudio::IScene void PostLoadStep() { IBufferManager &mgr = m_Context->GetBufferManager(); - forAllObjects<SImage>(m_GraphObjectList, GraphObjectTypes::Image, [&mgr](SImage *image){ + forAllObjects<SImage>(m_GraphObjectList, GraphObjectTypes::Image, + [&mgr](SImage *image, bool preferKtx){ if (image->m_ImagePath.IsValid() && qt3ds::runtime::isImagePath( image->m_ImagePath.c_str())) { const bool ibl = image->m_MappingMode == ImageMappingModes::LightProbe; - image->m_LoadedTextureData = mgr.CreateReloadableImage(image->m_ImagePath, - false, ibl); + const auto unified = IBufferManager::resolveImagePath( + QString::fromLatin1(image->m_ImagePath.c_str()), preferKtx); + image->m_LoadedTextureData = mgr.CreateReloadableImage(unified, false, ibl); image->m_LoadedTextureData->m_callbacks.push_back(image); } }); @@ -930,8 +932,8 @@ struct Qt3DSRenderSceneManager : public Q3DStudio::ISceneManager, const QVector<QString> slideSourcePaths = inParser->GetSlideSourcePaths(); IBufferManager &theManager(m_Context->m_Context->GetBufferManager()); // List of image paths to be loaded in parallel at the end. - eastl::vector<CRegisteredString> theSourcePathList; - eastl::vector<CRegisteredString> iblList; + QVector<CRegisteredString> theSourcePathList; + QVector<CRegisteredString> iblList; for (QT3DSU32 idx = 0, end = theSourcePathData.size(); idx < end; ++idx) { const eastl::string &theValue = theSourcePathData[idx]; CRegisteredString theSourcePath = @@ -962,23 +964,25 @@ struct Qt3DSRenderSceneManager : public Q3DStudio::ISceneManager, } } } - + const auto preferKtx = theScene->m_Presentation->m_preferKTX; + const auto flipCompressed = theScene->m_Presentation->m_flipCompressedTextures; + const auto contextType = m_Context->m_Context->GetRenderContext().GetRenderContextType(); // Fire off parallel loading of the source paths - QT3DSU64 imageBatchId = m_Context->m_Context->GetImageBatchLoader().LoadImageBatch( + auto &batchLoader = m_Context->m_Context->GetImageBatchLoader(); + theSourcePathList = IBufferManager::resolveSourcePaths(*m_Context->m_StringTable, + theSourcePathList, preferKtx); + iblList = IBufferManager::resolveSourcePaths(*m_Context->m_StringTable, iblList, + preferKtx); + + QT3DSU64 imageBatchId = batchLoader.LoadImageBatch( toConstDataRef(theSourcePathList.data(), theSourcePathList.size()), - CRegisteredString(), nullptr, m_Context->m_Context->GetRenderContext() - .GetRenderContextType(), - theScene->m_Presentation->m_preferKTX, false, - theScene->m_Presentation->m_flipCompressedTextures); - QT3DSU64 iblImageBatchId = m_Context->m_Context->GetImageBatchLoader().LoadImageBatch( + CRegisteredString(), nullptr, contextType, false, flipCompressed); + QT3DSU64 iblImageBatchId = batchLoader.LoadImageBatch( toConstDataRef(iblList.data(), iblList.size()), - CRegisteredString(), nullptr, m_Context->m_Context->GetRenderContext() - .GetRenderContextType(), - theScene->m_Presentation->m_preferKTX, true, - theScene->m_Presentation->m_flipCompressedTextures); - m_Context->m_Context->GetImageBatchLoader().BlockUntilLoaded( + CRegisteredString(), nullptr, contextType, true, flipCompressed); + batchLoader.BlockUntilLoaded( static_cast<TImageBatchId>(imageBatchId)); - m_Context->m_Context->GetImageBatchLoader().BlockUntilLoaded( + batchLoader.BlockUntilLoaded( static_cast<TImageBatchId>(iblImageBatchId)); theIScene = QT3DS_NEW(m_Context->GetAllocator(), @@ -1123,10 +1127,8 @@ struct Qt3DSRenderSceneManager : public Q3DStudio::ISceneManager, // this means graphics have been initialized eastl::string theSourcePathStr; IBufferManager &theManager(m_Context->m_Context->GetBufferManager()); - nvvector<CRegisteredString> imagePathList(m_Context->GetAllocator(), - "imagePathList"); - nvvector<CRegisteredString> iblImagePathList(m_Context->GetAllocator(), - "iblImagePathList"); + QVector<CRegisteredString> imagePathList; + QVector<CRegisteredString> iblImagePathList; for (QT3DSU32 idx = 0, end = m_SourcePaths.size(); idx < end; ++idx) { theSourcePathStr.assign(m_SourcePaths[idx].first); bool hasTransparency = m_SourcePaths[idx].second.first; @@ -1159,12 +1161,17 @@ struct Qt3DSRenderSceneManager : public Q3DStudio::ISceneManager, { QT3DS_PERF_SCOPED_TIMER(m_Context->m_CoreContext->GetPerfTimer(), "Binding: Initial Batch Image Load") + auto &batchLoader = m_Context->m_Context->GetImageBatchLoader(); + imagePathList = IBufferManager::resolveSourcePaths(*m_Context->m_StringTable, + imagePathList, pktx); + iblImagePathList = IBufferManager::resolveSourcePaths(*m_Context->m_StringTable, + iblImagePathList, pktx); - m_Context->m_Context->GetImageBatchLoader().LoadImageBatch( + batchLoader.LoadImageBatch( toConstDataRef(imagePathList.data(), imagePathList.size()), CRegisteredString(), nullptr, m_Context->m_Context->GetRenderContext() .GetRenderContextType(), pktx, false); - m_Context->m_Context->GetImageBatchLoader().LoadImageBatch( + batchLoader.LoadImageBatch( toConstDataRef(iblImagePathList.data(), iblImagePathList.size()), CRegisteredString(), nullptr, m_Context->m_Context->GetRenderContext() .GetRenderContextType(), pktx, true); diff --git a/src/runtime/Qt3DSApplication.cpp b/src/runtime/Qt3DSApplication.cpp index 6bc7b1b..5c6dbb7 100644 --- a/src/runtime/Qt3DSApplication.cpp +++ b/src/runtime/Qt3DSApplication.cpp @@ -350,11 +350,12 @@ struct STextureUploadRenderTask : public IRenderTask, public IImageLoadListener if (!m_deleteSet.isEmpty()) m_bufferManager.unloadSet(m_deleteSet); if (!m_uploadSet.isEmpty()) { - nvvector<CRegisteredString> sourcePaths(m_bufferManager.GetStringTable().GetAllocator(), - "TempSourcePathList"); + QVector<CRegisteredString> sourcePaths; for (auto &s : qAsConst(m_uploadSet)) sourcePaths.push_back(m_bufferManager.GetStringTable().RegisterStr(s)); - QT3DSU32 id = m_batchLoader.LoadImageBatch(sourcePaths, CRegisteredString(), + QT3DSU32 id = m_batchLoader.LoadImageBatch(toConstDataRef(sourcePaths.data(), + sourcePaths.size()), + CRegisteredString(), this, m_type, m_preferKtx, false); if (id) { m_batches[id] = m_uploadSet; @@ -362,11 +363,12 @@ struct STextureUploadRenderTask : public IRenderTask, public IImageLoadListener } } if (!m_uploadWaitSet.isEmpty()) { - nvvector<CRegisteredString> sourcePaths(m_bufferManager.GetStringTable().GetAllocator(), - "TempSourcePathList"); + QVector<CRegisteredString> sourcePaths; for (auto &s : qAsConst(m_uploadWaitSet)) sourcePaths.push_back(m_bufferManager.GetStringTable().RegisterStr(s)); - QT3DSU32 id = m_batchLoader.LoadImageBatch(sourcePaths, CRegisteredString(), + QT3DSU32 id = m_batchLoader.LoadImageBatch(toConstDataRef(sourcePaths.data(), + sourcePaths.size()), + CRegisteredString(), this, m_type, m_preferKtx, false); if (id) { m_batchLoader.BlockUntilLoaded(id); @@ -382,14 +384,16 @@ struct STextureUploadRenderTask : public IRenderTask, public IImageLoadListener m_uploadWaitSet.unite(set); else m_uploadSet.unite(set); - m_deleteSet.subtract(set); + const auto unified = IBufferManager::resolveImageSet(set, m_preferKtx); + m_deleteSet.subtract(unified); } void remove(const QSet<QString> &set) { + const auto unified = IBufferManager::resolveImageSet(set, m_preferKtx); QMutexLocker loc(&m_updateMutex); - m_uploadSet.subtract(set); - m_uploadWaitSet.subtract(set); - m_deleteSet.unite(set); + m_uploadSet.subtract(unified); + m_uploadWaitSet.subtract(unified); + m_deleteSet.unite(unified); } bool persistent() const override { @@ -402,7 +406,7 @@ struct STextureUploadRenderTask : public IRenderTask, public IImageLoadListener } void OnImageBatchComplete(QT3DSU64 inBatch) override { - m_bufferManager.loadSet(m_batches[inBatch]); + m_bufferManager.loadSet(m_batches[inBatch], m_flipCompressedTextures); } }; diff --git a/src/runtimerender/Qt3DSRenderInputStreamFactory.cpp b/src/runtimerender/Qt3DSRenderInputStreamFactory.cpp index 1a418b1..5b02164 100644 --- a/src/runtimerender/Qt3DSRenderInputStreamFactory.cpp +++ b/src/runtimerender/Qt3DSRenderInputStreamFactory.cpp @@ -205,6 +205,15 @@ struct SFactory : public IInputStreamFactory } return false; } + bool GetPathForStream(IRefCountedInputStream *stream, QString &outFile) override + { + if (stream) { + SInputStream *theRealStream = static_cast<SInputStream *>(stream); + outFile = theRealStream->m_Path; + return true; + } + return false; + } }; } diff --git a/src/runtimerender/Qt3DSRenderInputStreamFactory.h b/src/runtimerender/Qt3DSRenderInputStreamFactory.h index e82134b..fdc9e75 100644 --- a/src/runtimerender/Qt3DSRenderInputStreamFactory.h +++ b/src/runtimerender/Qt3DSRenderInputStreamFactory.h @@ -57,6 +57,7 @@ namespace render { // else returns false virtual bool GetPathForFile(const QString &inFilename, QString &outFile, bool inQuiet = false) = 0; + virtual bool GetPathForStream(IRefCountedInputStream *stream, QString &outFile) = 0; // Create an input stream factory using this foundation and an platform-optional app // directory diff --git a/src/runtimerender/graphobjects/Qt3DSRenderImage.cpp b/src/runtimerender/graphobjects/Qt3DSRenderImage.cpp index 44e7f12..458f3bf 100644 --- a/src/runtimerender/graphobjects/Qt3DSRenderImage.cpp +++ b/src/runtimerender/graphobjects/Qt3DSRenderImage.cpp @@ -67,7 +67,8 @@ static void HandleOffscreenResult(SImage &theImage, SImageTextureData &newImage, } bool SImage::ClearDirty(IBufferManager &inBufferManager, IOffscreenRenderManager &inRenderManager, - IRenderPluginManager &inPluginManager, bool forIbl, bool flipCompressed) + IRenderPluginManager &inPluginManager, bool preferKtx, bool forIbl, + bool flipCompressed) { bool wasDirty = m_Flags.IsDirty(); @@ -93,17 +94,22 @@ bool SImage::ClearDirty(IBufferManager &inBufferManager, IOffscreenRenderManager } } + // Do not recheck image path if we are not dirty and we already have a texture + if (!wasDirty) + newImage.m_Texture = m_TextureData.m_Texture; + if (newImage.m_Texture == nullptr) { m_LastFrameOffscreenRenderer = nullptr; if (m_ImagePath.IsValid() && !m_OffscreenRendererId.IsValid() && !inRenderManager.HasOffscreenRenderer(m_ImagePath)) { // Image has sourcepath set - if (!m_LoadedTextureData - || m_LoadedTextureData->m_path != QString::fromUtf8(m_ImagePath.c_str())) { + const QString unifiedPath = IBufferManager::resolveImagePath( + QString::fromUtf8(m_ImagePath.c_str()), preferKtx); + if (!m_LoadedTextureData || m_LoadedTextureData->m_path != unifiedPath) { if (m_LoadedTextureData) m_LoadedTextureData->m_callbacks.removeOne(this); forIbl = forIbl || m_MappingMode == ImageMappingModes::LightProbe; - m_LoadedTextureData = inBufferManager.CreateReloadableImage(m_ImagePath, false, + m_LoadedTextureData = inBufferManager.CreateReloadableImage(unifiedPath, false, forIbl, flipCompressed); m_LoadedTextureData->m_callbacks.push_back(this); } @@ -114,8 +120,8 @@ bool SImage::ClearDirty(IBufferManager &inBufferManager, IOffscreenRenderManager newImage.m_BSDFMipMap = m_LoadedTextureData->m_BSDFMipMap; } else if (m_Flags.IsForceLoad()) { QSet<QString> ls; - ls.insert(QString(m_ImagePath)); - inBufferManager.loadSet(ls); + ls.insert(unifiedPath); + inBufferManager.loadSet(ls, flipCompressed); newImage.m_Texture = m_LoadedTextureData->m_Texture; newImage.m_TextureFlags = m_LoadedTextureData->m_TextureFlags; newImage.m_BSDFMipMap = m_LoadedTextureData->m_BSDFMipMap; diff --git a/src/runtimerender/graphobjects/Qt3DSRenderImage.h b/src/runtimerender/graphobjects/Qt3DSRenderImage.h index 26b4377..04d890f 100644 --- a/src/runtimerender/graphobjects/Qt3DSRenderImage.h +++ b/src/runtimerender/graphobjects/Qt3DSRenderImage.h @@ -89,8 +89,8 @@ namespace render { // Or finds the image. // and sets up the texture transform bool ClearDirty(IBufferManager &inBufferManager, IOffscreenRenderManager &inRenderManager, - IRenderPluginManager &pluginManager, bool forIbl = false, - bool flipCompressed = false); + IRenderPluginManager &pluginManager, bool preferKtx, bool forIbl, + bool flipCompressed); void CalculateTextureTransform(); diff --git a/src/runtimerender/rendererimpl/Qt3DSRendererImplLayerRenderPreparationData.cpp b/src/runtimerender/rendererimpl/Qt3DSRendererImplLayerRenderPreparationData.cpp index 26d2c71..677440e 100644 --- a/src/runtimerender/rendererimpl/Qt3DSRendererImplLayerRenderPreparationData.cpp +++ b/src/runtimerender/rendererimpl/Qt3DSRendererImplLayerRenderPreparationData.cpp @@ -641,7 +641,9 @@ namespace render { qt3dsContext.GetOffscreenRenderManager()); IRenderPluginManager &theRenderPluginManager(qt3dsContext.GetRenderPluginManager()); if (inImage.ClearDirty(bufferManager, theOffscreenRenderManager, theRenderPluginManager, - false, m_Layer.m_Scene->m_Presentation->m_flipCompressedTextures)) + m_Layer.m_Scene->m_Presentation->m_preferKTX, + inImage.m_MappingMode == ImageMappingModes::LightProbe, + m_Layer.m_Scene->m_Presentation->m_flipCompressedTextures)) ioFlags |= RenderPreparationResultFlagValues::Dirty; // All objects with offscreen renderers are pickable so we can pass the pick through to the @@ -1180,7 +1182,9 @@ namespace render { IQt3DSRenderContext &theContext(m_Renderer.GetQt3DSContext()); return inLightProbe.ClearDirty(theContext.GetBufferManager(), theContext.GetOffscreenRenderManager(), - theContext.GetRenderPluginManager(), true); + theContext.GetRenderPluginManager(), + m_Layer.m_Scene->m_Presentation->m_preferKTX, true, + m_Layer.m_Scene->m_Presentation->m_flipCompressedTextures); } struct SLightNodeMarker diff --git a/src/runtimerender/resourcemanager/Qt3DSRenderBufferManager.cpp b/src/runtimerender/resourcemanager/Qt3DSRenderBufferManager.cpp index 43f7541..87fe54c 100644 --- a/src/runtimerender/resourcemanager/Qt3DSRenderBufferManager.cpp +++ b/src/runtimerender/resourcemanager/Qt3DSRenderBufferManager.cpp @@ -54,6 +54,207 @@ using namespace qt3ds::render; +namespace qt3ds { +namespace render { + +static const QString QT3DSTUDIO_TAG = QStringLiteral("qt3dstudio"); + +static QFileInfo matchCaseInsensitiveFile(const QString& file) +{ + const auto searchFromPaths = [](QString file) -> QFileInfo { + QFileInfo fileInfo(file); + if (fileInfo.exists()) + return fileInfo; + + const QStringList searchDirectories = QDir::searchPaths(QT3DSTUDIO_TAG); + for (const auto &directoryPath : searchDirectories) { + auto path = QDir::cleanPath(directoryPath + '/' + file); + QFileInfo info(path); + if (info.exists()) + return info; + } + return fileInfo; + }; + + QFileInfo info = searchFromPaths(file); + if (info.exists()) + return info; + + QString searchPath = file; + + // Trying relative to search directories. + // First remove any ../ and ./ from the path + if (searchPath.startsWith(QLatin1String("../"))) + searchPath = searchPath.right(searchPath.length() - 3); + else if (searchPath.startsWith(QLatin1String("./"))) + searchPath = searchPath.right(searchPath.length() - 2); + + int loops = 0; + while (++loops <= 4) { + info = searchFromPaths(searchPath); + if (info.exists()) + return info; + searchPath.prepend(QLatin1String("../")); + } + + return info; +} + +static bool checkFileExists(const QString &tryFile, bool relative, QString &outFile) +{ + QFileInfo info; + if (relative) + info = matchCaseInsensitiveFile(tryFile); + else + info.setFile(tryFile); + if (info.exists()) { + outFile = info.filePath(); + return true; + } + if (!relative) { + // Some textures, for example environment maps for custom materials, + // have absolute path at this point. It points to the wrong place with + // the new project structure, so we need to split it up and construct + // the new absolute path here. + QString wholePath = tryFile; + QStringList splitPath = wholePath.split(QLatin1String("../")); + if (splitPath.size() > 1) { + QString searchPath = splitPath.first() + splitPath.last(); + int loops = 0; + while (++loops <= 3) { + info.setFile(searchPath); + if (info.exists()) { + outFile = info.filePath(); + return true; + } + searchPath = splitPath.at(0); + for (int i = 0; i < loops; i++) + searchPath.append(QLatin1String("../")); + searchPath.append(splitPath.last()); + } + } + } + return false; +} + +// Locate existing file by adding a supported suffix to localFile. +static bool existingImageFileForPath(const QString &localFile, bool relative, bool preferKTX, + QString &outPath) +{ + // Do nothing if given filepath exists without suffix + QFileInfo fi(localFile); + if (fi.exists()) { + outPath = localFile; + return true; + } + + // Lists of supported image formats in preferred-first order. + const QStringList compressedFormats {"ktx", "astc", "dds"}; + const QStringList nonCompressedFormats {"hdr", "png", "jpg", "jpeg", "gif"}; + + // Depending on preferKTX, check compressed formats before or after non-compressed ones. + QStringList supportedFormats = preferKTX ? compressedFormats + nonCompressedFormats + : nonCompressedFormats + compressedFormats; + + // Check first if file exists from resources as that + // is common and optimal for integrity case. + for (const QString &suffix : supportedFormats) { + QString tryFile = ":/" + localFile + "." + suffix; + if (checkFileExists(tryFile, relative, outPath)) + return true; + } + // If not found, check still file path as-is + for (const QString &suffix : supportedFormats) { + QString tryFile = localFile + "." + suffix; + if (checkFileExists(tryFile, relative, outPath)) + return true; + } + + outPath = localFile; + return false; +} + +// Return resolved image path for unresolved source path. +// Handle preferKtx, search paths, empty suffixes, schemas etc. +QString IBufferManager::resolveImagePath(const QString &sourcePath, bool preferKtx) +{ + QString path = sourcePath; + QUrl urlPath(path); + + // Do nothing for image providers + if (urlPath.scheme() == QLatin1String("image")) + return sourcePath; + + // Normalize the path + path = CFileTools::NormalizePathForQtUsage(path); + urlPath.setUrl(path); + + const bool hasSuffix = urlPath.fileName().contains(QLatin1Char('.')); + const bool relative = urlPath.isRelative(); + + // Find an image file corresponding to only filename without suffix + if (!hasSuffix) { + QString outPath; + if (!existingImageFileForPath(path, relative, preferKtx, outPath)) { + qCWarning(WARNING, "Failed to resolve path %s", qPrintable(sourcePath)); + return {}; + } + return outPath; + } + + // First check ktx files if preferKtx is set + if (preferKtx) { + QString ktxSource = path; + const bool originalIsKtx = path.endsWith(QLatin1String("ktx"), Qt::CaseInsensitive); + if (!originalIsKtx) { + const int index = ktxSource.lastIndexOf(QLatin1Char('.')); + ktxSource = ktxSource.left(index); + ktxSource.append(QLatin1String(".ktx")); + } + QString result; + if (checkFileExists(ktxSource, relative, result)) + return result; + if (originalIsKtx) { + qCWarning(WARNING, "Failed to resolve path %s", qPrintable(sourcePath)); + return {}; + } + } + + QString result; + if (checkFileExists(path, relative, result)) + return result; + + qCWarning(WARNING, "Failed to resolve path %s", qPrintable(sourcePath)); + return {}; +} + +QSet<QString> IBufferManager::resolveImageSet(const QSet<QString> &set, bool preferKtx) +{ + QSet<QString> ret; + for (auto &sourcePath : set) { + auto unified = resolveImagePath(sourcePath, preferKtx); + if (!unified.isEmpty()) + ret.insert(unified); + } + return ret; +} + +QVector<CRegisteredString> IBufferManager::resolveSourcePaths( + IStringTable &strTable, const QVector<CRegisteredString> &sourcePaths, bool preferKtx) +{ + QVector<CRegisteredString> ret; + for (int i = 0; i < sourcePaths.size(); i++) { + QString path = QString::fromLatin1(sourcePaths[i].c_str()); + auto unified = strTable.RegisterStr(IBufferManager::resolveImagePath(path, preferKtx)); + if (unified.IsValid()) + ret.append(unified); + } + return ret; +} + +} +} + namespace { using eastl::hash; @@ -122,6 +323,7 @@ struct SBufferManager : public IBufferManager QHash<QString, QSharedPointer<QQmlImageProviderBase> > m_imageProviders; QHash<QString, ReloadableTexturePtr> m_reloadableTextures; + ReloadableTexturePtr m_nullTexture; static const char8_t *GetPrimitivesDirectory() { return "res//primitives"; } @@ -296,24 +498,25 @@ struct SBufferManager : public IBufferManager void loadSet(const QSet<QString> &imageSet, bool flipCompressed) override { - for (const auto &x : imageSet) { - if (!m_reloadableTextures.contains(x)) { - auto img = CreateReloadableImage(m_StrTable->RegisterStr(qPrintable(x)), false, - false, flipCompressed); - img->m_initialized = false; - loadTextureImage(*m_reloadableTextures[x]); - } else if (!m_reloadableTextures[x]->m_loaded) { - loadTextureImage(*m_reloadableTextures[x]); + for (const auto &sourcePath : imageSet) { + if (!m_reloadableTextures.contains(sourcePath)) { + auto img = CreateReloadableImage(sourcePath, false, false, flipCompressed); + if (img != m_nullTexture) { + img->m_initialized = false; + loadTextureImage(*m_reloadableTextures[sourcePath]); + } + } else if (!m_reloadableTextures[sourcePath]->m_loaded) { + loadTextureImage(*m_reloadableTextures[sourcePath]); } } } void unloadSet(const QSet<QString> &imageSet) override { - for (const auto &x : imageSet) { - if (m_reloadableTextures.contains(x)) { - if (m_reloadableTextures[x]->m_loaded) - unloadTextureImage(*m_reloadableTextures[x]); + for (const auto &sourcePath : imageSet) { + if (m_reloadableTextures.contains(sourcePath)) { + if (m_reloadableTextures[sourcePath]->m_loaded) + unloadTextureImage(*m_reloadableTextures[sourcePath]); } } } @@ -328,18 +531,20 @@ struct SBufferManager : public IBufferManager } } - virtual ReloadableTexturePtr CreateReloadableImage(CRegisteredString inSourcePath, + virtual ReloadableTexturePtr CreateReloadableImage(const QString &inSourcePath, bool inForceScanForTransparency, bool inBsdfMipmaps, bool flipCompressed) override { - // Remove schema and root from keys. To prevent loading twice file "qrc:///foo" and "foo" - QString path = QString::fromLatin1(inSourcePath.c_str()); - path = QUrl(path).path(); - if (path.startsWith(QLatin1String("/"))) - path = path.right(path.length() - 1); - + const auto path = inSourcePath; const bool inserted = m_reloadableTextures.contains(path); + if (path.isNull()) { + if (m_nullTexture.isNull()) { + m_nullTexture = ReloadableTexturePtr::create(); + m_nullTexture->m_initialized = true; + } + return m_nullTexture; + } if (!inserted || (inserted && m_reloadableTextures[path]->m_initialized == false)) { if (!inserted) m_reloadableTextures.insert(path, ReloadableTexturePtr::create()); @@ -361,13 +566,13 @@ struct SBufferManager : public IBufferManager // Failed to look up image from map, perhaps external of UIP/UIA image // Try with full URI and load from filesystem. if (theIter == m_ImageMap.end()) - theIter = m_ImageMap.find(getImagePath(QString::fromLatin1(inSourcePath.c_str()))); + theIter = m_ImageMap.find(getImagePath(inSourcePath)); if (theIter == m_ImageMap.end()) { loadTextureImage(*m_reloadableTextures[path], flipCompressed); imagePath = getImagePath(path); theIter = m_ImageMap.find(imagePath); if (theIter == m_ImageMap.end()) - theIter = m_ImageMap.find(getImagePath(QString::fromLatin1(inSourcePath.c_str()))); + theIter = m_ImageMap.find(getImagePath(inSourcePath)); } #endif if (theIter != m_ImageMap.end()) { @@ -390,7 +595,7 @@ struct SBufferManager : public IBufferManager QT3DS_PERF_SCOPED_TIMER(m_PerfTimer, "BufferManager: Image Decompression") theLoadedImage = SLoadedTexture::Load( inImagePath.c_str(), m_Context->GetFoundation(), *m_InputStreamFactory, - true, inFlipCompressed, m_Context->GetRenderContextType(), false, this); + true, inFlipCompressed, m_Context->GetRenderContextType(), this); // Hackish solution to custom materials not finding their textures if they are used // in sub-presentations. if (!theLoadedImage) { @@ -403,7 +608,7 @@ struct SBufferManager : public IBufferManager theLoadedImage = SLoadedTexture::Load( searchPathRel.toUtf8(), m_Context->GetFoundation(), *m_InputStreamFactory, true, false, - m_Context->GetRenderContextType(), false, this); + m_Context->GetRenderContextType(), this); } if (!theLoadedImage) { @@ -415,7 +620,7 @@ struct SBufferManager : public IBufferManager theLoadedImage = SLoadedTexture::Load( searchPath.toUtf8(), m_Context->GetFoundation(), *m_InputStreamFactory, true, false, - m_Context->GetRenderContextType(), false, this); + m_Context->GetRenderContextType(), this); searchPath.prepend(QLatin1String("../")); } } @@ -435,7 +640,7 @@ struct SBufferManager : public IBufferManager theLoadedImage = SLoadedTexture::Load( searchPath.toUtf8(), m_Context->GetFoundation(), *m_InputStreamFactory, true, false, - m_Context->GetRenderContextType(), false, this); + m_Context->GetRenderContextType(), this); searchPath = splitPath.at(0); for (int i = 0; i < loops; i++) searchPath.append(QLatin1String("../")); diff --git a/src/runtimerender/resourcemanager/Qt3DSRenderBufferManager.h b/src/runtimerender/resourcemanager/Qt3DSRenderBufferManager.h index 0debd9c..159e02b 100644 --- a/src/runtimerender/resourcemanager/Qt3DSRenderBufferManager.h +++ b/src/runtimerender/resourcemanager/Qt3DSRenderBufferManager.h @@ -79,6 +79,12 @@ namespace render { bool inIgnoreIfLoaded) = 0; virtual void UnaliasImagePath(CRegisteredString inSourcePath) = 0; + // Return resolved imagepaths from unresolved sourcepaths + static QString resolveImagePath(const QString &sourcePath, bool preferKtx); + static QSet<QString> resolveImageSet(const QSet<QString> &set, bool preferKtx); + static QVector<CRegisteredString> resolveSourcePaths(IStringTable &strTable, + const QVector<CRegisteredString> &sourcePaths, bool preferKtx); + // Returns the given source path unless the source path is aliased; in which case returns // the aliased path. virtual CRegisteredString GetImagePath(CRegisteredString inSourcePath) = 0; @@ -93,15 +99,15 @@ namespace render { bool inForceScanForTransparency = false, bool inBsdfMipmaps = false) = 0; - virtual ReloadableTexturePtr CreateReloadableImage(CRegisteredString inSourcePath, + virtual ReloadableTexturePtr CreateReloadableImage(const QString &inSourcePath, bool inForceScanForTransparency = false, bool inBsdfMipmaps = false, bool flipCompressed = false) = 0; virtual void enableReloadableResources(bool enable) = 0; virtual bool isReloadableResourcesEnabled() const = 0; - virtual void loadSet(const QSet<QString> &imageSet, bool flipCompressed = false) = 0; - virtual void unloadSet(const QSet<QString> &imageSet) = 0; + virtual void loadSet(const QSet<QString> &unifiedImageSet, bool flipCompressed) = 0; + virtual void unloadSet(const QSet<QString> &unifiedImageSet) = 0; virtual void reloadAll(bool flipCompressed) = 0; virtual void loadCustomMesh(const QString &name, qt3dsimp::Mesh *mesh) = 0; diff --git a/src/runtimerender/resourcemanager/Qt3DSRenderImageBatchLoader.cpp b/src/runtimerender/resourcemanager/Qt3DSRenderImageBatchLoader.cpp index 28e0e4a..01ec9d4 100644 --- a/src/runtimerender/resourcemanager/Qt3DSRenderImageBatchLoader.cpp +++ b/src/runtimerender/resourcemanager/Qt3DSRenderImageBatchLoader.cpp @@ -108,7 +108,6 @@ struct SImageLoaderBatch QT3DSU32 m_FinalizedImageCount; QT3DSU32 m_NumImages; NVRenderContextType m_contextType; - bool m_preferKTX; bool m_flipCompressedTextures; bool m_ibl; @@ -118,14 +117,14 @@ struct SImageLoaderBatch CRegisteredString inImageTillLoaded, IImageLoadListener *inListener, NVRenderContextType contextType, - bool preferKTX, bool iblImage, + bool iblImage, bool flipCompressedTextures); // Called from main thread SImageLoaderBatch(SBatchLoader &inLoader, IImageLoadListener *inLoadListener, const TLoadingImageList &inImageList, TImageBatchId inBatchId, - QT3DSU32 inImageCount, NVRenderContextType contextType, - bool preferKTX, bool ibl, bool flipCompressedTextures); + QT3DSU32 inImageCount, NVRenderContextType contextType, bool ibl, + bool flipCompressedTextures); // Called from main thread ~SImageLoaderBatch(); @@ -265,7 +264,7 @@ struct SBatchLoader : public IImageBatchLoader CRegisteredString inImageTillLoaded, IImageLoadListener *inListener, NVRenderContextType contextType, - bool preferKTX, bool iblImages, + bool iblImages, bool flipCompressedTextures) override { if (inSourcePaths.size() == 0) @@ -282,7 +281,7 @@ struct SBatchLoader : public IImageBatchLoader SImageLoaderBatch *theBatch(SImageLoaderBatch::CreateLoaderBatch( *this, theBatchId, inSourcePaths, inImageTillLoaded, inListener, contextType, - preferKTX, iblImages, flipCompressedTextures)); + iblImages, flipCompressedTextures)); if (theBatch) { m_Batches.insert(eastl::make_pair(theBatchId, theBatch)); return theBatchId; @@ -394,7 +393,6 @@ void SLoadingImage::LoadImage(void *inImg) theThis->m_Batch->m_Loader.m_InputStreamFactory, true, theThis->m_Batch->m_flipCompressedTextures, theThis->m_Batch->m_contextType, - theThis->m_Batch->m_preferKTX, &theThis->m_Batch->m_Loader.m_BufferManager); // if ( theTexture ) // theTexture->EnsureMultiplerOfFour( theThis->m_Batch->m_Loader.m_Foundation, @@ -437,8 +435,7 @@ SImageLoaderBatch::CreateLoaderBatch(SBatchLoader &inLoader, TImageBatchId inBat NVConstDataRef<CRegisteredString> inSourcePaths, CRegisteredString inImageTillLoaded, IImageLoadListener *inListener, - NVRenderContextType contextType, - bool preferKTX, bool iblImages, + NVRenderContextType contextType, bool iblImages, bool flipCompressedTextures) { TLoadingImageList theImages; @@ -477,7 +474,7 @@ SImageLoaderBatch::CreateLoaderBatch(SBatchLoader &inLoader, TImageBatchId inBat (SImageLoaderBatch *)inLoader.m_BatchPool.allocate(__FILE__, __LINE__); new (theBatch) SImageLoaderBatch(inLoader, inListener, theImages, inBatchId, theLoadingImageCount, - contextType, preferKTX, iblImages, flipCompressedTextures); + contextType, iblImages, flipCompressedTextures); return theBatch; } return NULL; @@ -486,7 +483,7 @@ SImageLoaderBatch::CreateLoaderBatch(SBatchLoader &inLoader, TImageBatchId inBat SImageLoaderBatch::SImageLoaderBatch(SBatchLoader &inLoader, IImageLoadListener *inLoadListener, const TLoadingImageList &inImageList, TImageBatchId inBatchId, QT3DSU32 inImageCount, NVRenderContextType contextType, - bool preferKTX, bool ibl, bool flipCompressedTextures) + bool ibl, bool flipCompressedTextures) : m_Loader(inLoader) , m_LoadListener(inLoadListener) , m_LoadEvent(inLoader.m_Foundation.getAllocator()) @@ -497,7 +494,6 @@ SImageLoaderBatch::SImageLoaderBatch(SBatchLoader &inLoader, IImageLoadListener , m_FinalizedImageCount(0) , m_NumImages(inImageCount) , m_contextType(contextType) - , m_preferKTX(preferKTX) , m_ibl(ibl) , m_flipCompressedTextures(flipCompressedTextures) { diff --git a/src/runtimerender/resourcemanager/Qt3DSRenderImageBatchLoader.h b/src/runtimerender/resourcemanager/Qt3DSRenderImageBatchLoader.h index 64caa21..03b5599 100644 --- a/src/runtimerender/resourcemanager/Qt3DSRenderImageBatchLoader.h +++ b/src/runtimerender/resourcemanager/Qt3DSRenderImageBatchLoader.h @@ -73,7 +73,7 @@ namespace render { CRegisteredString inImageTillLoaded, IImageLoadListener *inListener, NVRenderContextType type, - bool preferKTX, bool iblImages, + bool iblImages, bool flipCompressedTextures = false) = 0; // Blocks if any of the images in the batch are in flight virtual void CancelImageBatchLoading(TImageBatchId inBatchId) = 0; diff --git a/src/runtimerender/resourcemanager/Qt3DSRenderLoadedTexture.cpp b/src/runtimerender/resourcemanager/Qt3DSRenderLoadedTexture.cpp index 8046a10..634e3c0 100644 --- a/src/runtimerender/resourcemanager/Qt3DSRenderLoadedTexture.cpp +++ b/src/runtimerender/resourcemanager/Qt3DSRenderLoadedTexture.cpp @@ -908,78 +908,26 @@ void SLoadedTexture::ReleaseDecompressedTexture(STextureData inImage) m_Allocator.deallocate(inImage.data); } -// Locate existing file by adding a supported suffix to localFile. -// If localFile already contains suffix or exiting file isn't found -// returns localFile unchanged. -static QString existingImageFileForPath(const QString &localFile, bool preferKTX) -{ - // Do nothing if given filepath exists or already has a suffix - QFileInfo fi(localFile); - if (!fi.suffix().isEmpty() || fi.exists()) - return localFile; - - // Lists of supported image formats in preferred-first order. - const QStringList compressedFormats {"ktx", "astc", "dds"}; - const QStringList nonCompressedFormats {"hdr", "png", "jpg", "jpeg", "gif"}; - - // Depending on preferKTX, check compressed formats before or after non-compressed ones. - QStringList supportedFormats = preferKTX ? compressedFormats + nonCompressedFormats - : nonCompressedFormats + compressedFormats; - - // Check first if file exists from resources as that - // is common and optimal for integrity case. - for (const QString &suffix : supportedFormats) { - QString tryFile = ":/" + localFile + "." + suffix; - if (QFileInfo::exists(tryFile)) - return tryFile; - } - // If not found, check still file path as-is - for (const QString &suffix : supportedFormats) { - QString tryFile = localFile + "." + suffix; - if (QFileInfo::exists(tryFile)) - return tryFile; - } - - return localFile; -} - -SLoadedTexture *SLoadedTexture::Load(const QString &inPath, NVFoundationBase &inFoundation, +SLoadedTexture *SLoadedTexture::Load(const QString &path, NVFoundationBase &inFoundation, IInputStreamFactory &inFactory, bool inFlipY, bool inFlipCompressed, - NVRenderContextType renderContextType, bool preferKTX, + NVRenderContextType renderContextType, IBufferManager *bufferManager) { - if (inPath.isEmpty()) + if (path.isEmpty()) return nullptr; - if (QUrl(inPath).scheme() == QLatin1String("image")) - return LoadQImage(inPath, inFlipY, inFoundation, renderContextType, bufferManager); + if (QUrl(path).scheme() == QLatin1String("image")) + return LoadQImage(path, inFlipY, inFoundation, renderContextType, bufferManager); - // If inPath doesn't have suffix, try to find most optimal existing one - QString path = existingImageFileForPath(inPath, preferKTX); + SLoadedTexture *theLoadedImage = nullptr; + NVScopedRefCounted<IRefCountedInputStream> theStream(inFactory.GetStreamForFile(path, true)); - // If preferKTX is true, always check KTX path first - QString ktxSource = path; - if (preferKTX) { - ktxSource = ktxSource.left(ktxSource.lastIndexOf(QLatin1Char('.'))); - ktxSource.append(QLatin1String(".ktx")); - } + if (!theStream.mPtr) + return nullptr; - SLoadedTexture *theLoadedImage = nullptr; - // We will get invalid error logs of files not found if we don't force quiet mode - // If the file is actually missing, it will be logged later (loaded image is null) - NVScopedRefCounted<IRefCountedInputStream> theStream( - inFactory.GetStreamForFile(preferKTX ? ktxSource : path, true)); - if (!theStream.mPtr) { - if (preferKTX) - theStream = inFactory.GetStreamForFile(path, true); - else - return nullptr; - } else { - path = ktxSource; - } QString fileName; - inFactory.GetPathForFile(path, fileName, true); + inFactory.GetPathForStream(theStream.mPtr, fileName); if (theStream.mPtr && path.size() > 3) { if (path.endsWith(QLatin1String("png"), Qt::CaseInsensitive) || path.endsWith(QLatin1String("jpg"), Qt::CaseInsensitive) @@ -1006,7 +954,7 @@ SLoadedTexture *SLoadedTexture::Load(const QString &inPath, NVFoundationBase &in inFlipCompressed); theLoadedImage = LoadASTC(fileName, inFlipY, inFoundation, renderContextType); } else { - qCWarning(INTERNAL_ERROR, "Unrecognized image extension: %s", qPrintable(inPath)); + qCWarning(INTERNAL_ERROR, "Unrecognized image extension: %s", qPrintable(path)); } } diff --git a/src/runtimerender/resourcemanager/Qt3DSRenderLoadedTexture.h b/src/runtimerender/resourcemanager/Qt3DSRenderLoadedTexture.h index 44d07de..c2aca9e 100644 --- a/src/runtimerender/resourcemanager/Qt3DSRenderLoadedTexture.h +++ b/src/runtimerender/resourcemanager/Qt3DSRenderLoadedTexture.h @@ -157,7 +157,6 @@ namespace render { bool inFlipCompressed = false, NVRenderContextType renderContextType = NVRenderContextValues::NullContext, - bool preferKTX = false, IBufferManager *bufferManager = nullptr); static SLoadedTexture *LoadDDS(IInStream &inStream, QT3DSI32 flipVertical, NVFoundationBase &fnd, |