diff options
| author | lucy96chen <47800040+lucy96chen@users.noreply.github.com> | 2022-11-29 12:35:54 -0800 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2022-11-29 12:35:54 -0800 |
| commit | d85c7b809d02e6dc0844aab07e66a6bac2462017 (patch) | |
| tree | 5b0c094e705df8b1c0a98de26e3d28514b014d80 | |
| parent | d60c925229cf911b0363bf9d5e25a6f73c6d5737 (diff) | |
FileStream-based implementation for updating cache index file (#2485)
* Draft FileStream-based implementation for updating cache file
* File streams fully integrated into shader cache code paths; Tests will not run unless file system is on disk as file streams do not play nicely with in-memory
* Brought old code back as fallback path, but tests need to ensure previous is freed first
* Testing structure updated, beginning cleanup work
* All tests working
* Cleanup changes
* Removed an extra tab at the end of a line
* Cleanup change
* Undo externals change
* Removed redundant logic for OS vs memory file system handling of the shader cache; Removed extra helper function left over from old cache implementation
* Reverted performance change to generate contents hashes when modules are being loaded as this code path is not always followed; Contents hashing now uses a combination of hashing and checking the last modified time for all file dependencies, only reading in and hashing the contents of all files if the last modified hash does not match
* Added handling to Module::updateContentsBasedHash for file dependencies which are not from a physical source file on disk; Added test for above
Co-authored-by: Lucy Chen <lucchen@nvidia.com>
Co-authored-by: Yong He <yonghe@outlook.com>
| -rw-r--r-- | slang.h | 5 | ||||
| -rwxr-xr-x | source/slang/slang-compiler.h | 10 | ||||
| -rw-r--r-- | source/slang/slang-preprocessor.cpp | 5 | ||||
| -rw-r--r-- | source/slang/slang-preprocessor.h | 2 | ||||
| -rw-r--r-- | source/slang/slang.cpp | 59 | ||||
| -rw-r--r-- | tools/gfx-unit-test/gfx-test-util.cpp | 15 | ||||
| -rw-r--r-- | tools/gfx-unit-test/gfx-test-util.h | 5 | ||||
| -rw-r--r-- | tools/gfx-unit-test/shader-cache-tests.cpp | 230 | ||||
| -rw-r--r-- | tools/gfx/persistent-shader-cache.cpp | 263 | ||||
| -rw-r--r-- | tools/gfx/persistent-shader-cache.h | 65 | ||||
| -rw-r--r-- | tools/gfx/renderer-shared.cpp | 17 |
11 files changed, 489 insertions, 187 deletions
@@ -4152,6 +4152,11 @@ namespace slang && values[3] == rhs.values[3]; } + bool operator!=(const Digest& rhs) + { + return !(*this == rhs); + } + uint32_t getHashCode() { return values[0]; diff --git a/source/slang/slang-compiler.h b/source/slang/slang-compiler.h index 14ac63531..07e611355 100755 --- a/source/slang/slang-compiler.h +++ b/source/slang/slang-compiler.h @@ -1366,14 +1366,6 @@ namespace Slang /// void setIRModule(IRModule* irModule) { m_irModule = irModule; } - DigestBuilder& getContentsDigestBuilder() { return contentsBuilder; } - - /// Set the contents digest for this module. - void setContentsDigest(slang::Digest digest) { contentsDigest = digest; } - - /// Get the contents digest for this module. - slang::Digest getContentsDigest() { return contentsDigest; } - Index getEntryPointCount() SLANG_OVERRIDE { return 0; } RefPtr<EntryPoint> getEntryPoint(Index index) SLANG_OVERRIDE { SLANG_UNUSED(index); return nullptr; } String getEntryPointMangledName(Index index) SLANG_OVERRIDE { SLANG_UNUSED(index); return String(); } @@ -1483,7 +1475,7 @@ namespace Slang StringSlicePool m_mangledExportPool; List<NodeBase*> m_mangledExportSymbols; - DigestBuilder contentsBuilder; + slang::Digest lastModifiedDigest; slang::Digest contentsDigest; }; typedef Module LoadedModule; diff --git a/source/slang/slang-preprocessor.cpp b/source/slang/slang-preprocessor.cpp index c977e44ab..fca8f5029 100644 --- a/source/slang/slang-preprocessor.cpp +++ b/source/slang/slang-preprocessor.cpp @@ -32,10 +32,9 @@ void PreprocessorHandler::handleEndOfTranslationUnit(Preprocessor* preprocessor) SLANG_UNUSED(preprocessor); } -void PreprocessorHandler::handleFileDependency(String const& path, ISlangBlob* contents) +void PreprocessorHandler::handleFileDependency(String const& path) { SLANG_UNUSED(path); - SLANG_UNUSED(contents); } // In order to simplify the naming scheme, we will nest the implementaiton of the @@ -2973,7 +2972,7 @@ static SlangResult readFile( // if( auto handler = context->m_preprocessor->handler ) { - handler->handleFileDependency(path, *outBlob); + handler->handleFileDependency(path); } return SLANG_OK; diff --git a/source/slang/slang-preprocessor.h b/source/slang/slang-preprocessor.h index 90e03e964..4d7721d31 100644 --- a/source/slang/slang-preprocessor.h +++ b/source/slang/slang-preprocessor.h @@ -28,7 +28,7 @@ using preprocessor::Preprocessor; struct PreprocessorHandler { virtual void handleEndOfTranslationUnit(Preprocessor* preprocessor); - virtual void handleFileDependency(String const& path, ISlangBlob* contents = nullptr); + virtual void handleFileDependency(String const& path); }; /// Description of a preprocessor options/dependencies diff --git a/source/slang/slang.cpp b/source/slang/slang.cpp index 361f1b193..5add881d6 100644 --- a/source/slang/slang.cpp +++ b/source/slang/slang.cpp @@ -49,6 +49,8 @@ #include "../../slang-tag-version.h" +#include <sys/stat.h> + // Used to print exception type names in internal-compiler-error messages #include <typeinfo> @@ -1898,13 +1900,9 @@ protected: // by applications to decide when they need to "hot reload" // their shader code. // - void handleFileDependency(String const& path, ISlangBlob* sourceBlob) SLANG_OVERRIDE + void handleFileDependency(String const& path) SLANG_OVERRIDE { m_module->addFilePathDependency(path); - if (sourceBlob) - { - m_module->getContentsDigestBuilder().addToDigest(sourceBlob); - } } // The second task that this handler deals with is detecting @@ -3046,10 +3044,6 @@ RefPtr<Module> Linkage::loadModule( return nullptr; } - auto builder = module->getContentsDigestBuilder(); - builder.addToDigest(sourceBlob); - module->setContentsDigest(builder.finalize()); - return module; } @@ -3269,7 +3263,52 @@ void Module::updateDependencyBasedHash( void Module::updateContentsBasedHash(DigestBuilder& builder) { - builder.addToDigest(getContentsDigest()); + auto filePathDependencies = getFilePathDependencies(); + + DigestBuilder lastModifiedBuilder; + auto statFailed = false; + for (auto file : filePathDependencies) + { + struct stat fileStatus; + auto res = stat(file.getBuffer(), &fileStatus); + if (res != 0) + { + statFailed = true; + break; + } + lastModifiedBuilder.addToDigest(fileStatus.st_mtime); + } + + slang::Digest temp = lastModifiedBuilder.finalize(); + if (statFailed || temp != lastModifiedDigest) + { + // Either a stat() call failed, or changes were made to at least one of the file dependencies, + // so we will need to re-generate the contents digest and save the new digest. + DigestBuilder contentsBuilder; + for (auto file : filePathDependencies) + { + List<uint8_t> fileContents; + if (SLANG_FAILED(File::readAllBytes(file, fileContents))) + { + // Failure to read the file means this is a digest for the contents of a source + // file which does not live on disk. + contentsBuilder.addToDigest(DigestUtil::fromString(file.getUnownedSlice())); + } + else + { + contentsBuilder.addToDigest(fileContents); + } + } + contentsDigest = contentsBuilder.finalize(); + if (!statFailed) + { + // If no stat() calls failed, then we have a valid last modified digest and should + // update the one we have saved. + lastModifiedDigest = temp; + } + } + + builder.addToDigest(contentsDigest); } void Module::addModuleDependency(Module* module) diff --git a/tools/gfx-unit-test/gfx-test-util.cpp b/tools/gfx-unit-test/gfx-test-util.cpp index fa0745872..116b4222a 100644 --- a/tools/gfx-unit-test/gfx-test-util.cpp +++ b/tools/gfx-unit-test/gfx-test-util.cpp @@ -72,6 +72,21 @@ namespace gfx_test return SLANG_OK; } + Slang::Result loadComputeProgramFromSource( + gfx::IDevice* device, + Slang::ComPtr<gfx::IShaderProgram>& outShaderProgram, + Slang::String source) + { + Slang::ComPtr<slang::IBlob> diagnosticsBlob; + + gfx::IShaderProgram::CreateDesc2 programDesc = {}; + programDesc.sourceType = gfx::ShaderModuleSourceType::SlangSource; + programDesc.sourceData = (void*)source.getBuffer(); + programDesc.sourceDataSize = source.getLength(); + + return device->createProgram2(programDesc, outShaderProgram.writeRef(), diagnosticsBlob.writeRef()); + } + Slang::Result loadGraphicsProgram( gfx::IDevice* device, Slang::ComPtr<gfx::IShaderProgram>& outShaderProgram, diff --git a/tools/gfx-unit-test/gfx-test-util.h b/tools/gfx-unit-test/gfx-test-util.h index 5175366c4..d11d5623c 100644 --- a/tools/gfx-unit-test/gfx-test-util.h +++ b/tools/gfx-unit-test/gfx-test-util.h @@ -18,6 +18,11 @@ namespace gfx_test const char* entryPointName, slang::ProgramLayout*& slangReflection); + Slang::Result loadComputeProgramFromSource( + gfx::IDevice* device, + Slang::ComPtr<gfx::IShaderProgram>& outShaderProgram, + Slang::String source); + Slang::Result loadGraphicsProgram( gfx::IDevice* device, Slang::ComPtr<gfx::IShaderProgram>& outShaderProgram, diff --git a/tools/gfx-unit-test/shader-cache-tests.cpp b/tools/gfx-unit-test/shader-cache-tests.cpp index a7306599a..c1d058d70 100644 --- a/tools/gfx-unit-test/shader-cache-tests.cpp +++ b/tools/gfx-unit-test/shader-cache-tests.cpp @@ -24,6 +24,7 @@ namespace gfx_test RenderApiFlag::Enum api; ComPtr<IDevice> device; + ComPtr<IShaderCacheStatistics> shaderCacheStats; ComPtr<IPipelineState> pipelineState; ComPtr<IResourceView> bufferView; @@ -33,12 +34,11 @@ namespace gfx_test // // - diskFileSystem - Used to save any files that must exist on disk for subsequent // save/load function calls (most prominently loadComputeProgram()) to pick up. - // - cacheFileSystem - Used to hold for the actual cache for all tests. This removes the need to - // manually clean out old cache files from previous test runs as it is - // located in-memory. This is the file system passed to device creation - // as part of the shader cache desc. + // This is also used to test the file stream implementation for the cache. + // - memoryFileSystem - Used to test the fallback path for the cache in the case physical + // file paths cannot be obtained, which prevents usage of file streams. ComPtr<ISlangMutableFileSystem> diskFileSystem; - ComPtr<ISlangMutableFileSystem> cacheFileSystem; + ComPtr<ISlangMutableFileSystem> memoryFileSystem; // Simple compute shaders we can pipe to our individual shader files for cache testing String contentsA = String( @@ -114,6 +114,7 @@ namespace gfx_test bufferView = nullptr; pipelineState = nullptr; device = nullptr; + shaderCacheStats = nullptr; } // TODO: This should be removed at some point. Currently exists as a workaround for module loading @@ -152,11 +153,10 @@ namespace gfx_test SLANG_IGNORE_TEST } - cacheFileSystem = new MemoryFileSystem(); + memoryFileSystem = new MemoryFileSystem(); diskFileSystem = OSFileSystem::getMutableSingleton(); - diskFileSystem = new RelativeFileSystem(diskFileSystem, "tools/gfx-unit-test"); - - shaderCache.shaderCacheFileSystem = cacheFileSystem; + diskFileSystem->createDirectory("tools/gfx-unit-test/shader-cache-test"); + diskFileSystem = new RelativeFileSystem(diskFileSystem, "tools/gfx-unit-test/shader-cache-test"); } void submitGPUWork() @@ -185,6 +185,44 @@ namespace gfx_test queue->executeCommandBuffer(commandBuffer); queue->waitOnHost(); } + + void cleanUpFiles() + { + freeOldResources(); + + List<String> filePaths; + diskFileSystem->enumeratePathContents( + ".", + [](SlangPathType pathType, const char* name, void* userData) + { + if (pathType == SlangPathType::SLANG_PATH_TYPE_FILE) + { + List<String>& out = *(List<String>*)userData; + out.add(String(name)); + } + }, + &filePaths); + + for (auto file : filePaths) + { + diskFileSystem->remove(file.getBuffer()); + } + // Get a mutable singleton so we can delete the folder. + auto fileSystem = OSFileSystem::getMutableSingleton(); + fileSystem->remove("tools/gfx-unit-test/shader-cache-test"); + } + + void run() + { + shaderCache.shaderCacheFileSystem = diskFileSystem; + runTests(); + shaderCache.shaderCacheFileSystem = memoryFileSystem; + runTests(); + + cleanUpFiles(); + } + + virtual void runTests() = 0; }; // Due to needing a workaround to prevent loading old, outdated modules, we need to @@ -205,7 +243,7 @@ namespace gfx_test ComPtr<IShaderProgram> shaderProgram; slang::ProgramLayout* slangReflection; - GFX_CHECK_CALL_ABORT(loadComputeProgram(device, shaderProgram, "test-tmp-single-entry", "computeMain", slangReflection)); + GFX_CHECK_CALL_ABORT(loadComputeProgram(device, shaderProgram, "shader-cache-test/test-tmp-single-entry", "computeMain", slangReflection)); ComputePipelineStateDesc pipelineDesc = {}; pipelineDesc.program = shaderProgram.get(); @@ -213,10 +251,8 @@ namespace gfx_test device->createComputePipelineState(pipelineDesc, pipelineState.writeRef())); } - void run() + void runTests() { - ComPtr<IShaderCacheStatistics> shaderCacheStats; - generateNewDevice(); createRequiredResources(); generateNewPipelineState(contentsA); @@ -275,13 +311,13 @@ namespace gfx_test switch (shaderIndex) { case 0: - shaderFilename = "test-tmp-multi-entry-A"; + shaderFilename = "shader-cache-test/test-tmp-multi-entry-A"; break; case 1: - shaderFilename = "test-tmp-multi-entry-B"; + shaderFilename = "shader-cache-test/test-tmp-multi-entry-B"; break; case 2: - shaderFilename = "test-tmp-multi-entry-C"; + shaderFilename = "shader-cache-test/test-tmp-multi-entry-C"; break; default: // Should never reach this point since we wrote the test @@ -305,10 +341,8 @@ namespace gfx_test submitGPUWork(); } - void run() + void runTests() { - ComPtr<IShaderCacheStatistics> shaderCacheStats; - generateNewDevice(); createRequiredResources(); modifyShaderA(contentsA); @@ -385,10 +419,8 @@ namespace gfx_test device->createComputePipelineState(pipelineDesc, pipelineState.writeRef())); } - void run() + void runTests() { - ComPtr<IShaderCacheStatistics> shaderCacheStats; - generateNewDevice(); createRequiredResources(); generateNewPipelineState(0); @@ -513,7 +545,7 @@ namespace gfx_test { ComPtr<IShaderProgram> shaderProgram; slang::ProgramLayout* slangReflection; - GFX_CHECK_CALL_ABORT(loadComputeProgram(device, shaderProgram, "test-tmp-importing", "computeMain", slangReflection)); + GFX_CHECK_CALL_ABORT(loadComputeProgram(device, shaderProgram, "shader-cache-test/test-tmp-importing", "computeMain", slangReflection)); ComputePipelineStateDesc pipelineDesc = {}; pipelineDesc.program = shaderProgram.get(); @@ -521,10 +553,8 @@ namespace gfx_test device->createComputePipelineState(pipelineDesc, pipelineState.writeRef())); } - void run() + void runTests() { - ComPtr<IShaderCacheStatistics> shaderCacheStats; - generateNewDevice(); createRequiredResources(); initializeFiles(); @@ -652,10 +682,8 @@ namespace gfx_test device->createComputePipelineState(pipelineDesc, pipelineState.writeRef())); } - void run() + void runTests() { - ComPtr<IShaderCacheStatistics> shaderCacheStats; - generateNewDevice(); createRequiredResources(); generateNewPipelineState(); @@ -837,15 +865,13 @@ namespace gfx_test queue->waitOnHost(); } - void run() + void runTests() { generateNewDevice(); createShaderProgram(); createRequiredResources(); submitGPUWork(); - ComPtr<IShaderCacheStatistics> shaderCacheStats; - device->queryInterface(SLANG_UUID_IShaderCacheStatistics, (void**)shaderCacheStats.writeRef()); SLANG_CHECK(shaderCacheStats->getCacheMissCount() == 2); SLANG_CHECK(shaderCacheStats->getCacheHitCount() == 0); @@ -930,15 +956,13 @@ namespace gfx_test return SLANG_OK; } - void run() + void runTests() { generateNewDevice(); createShaderProgram(); createRequiredResources(); submitGPUWork(); - ComPtr<IShaderCacheStatistics> shaderCacheStats; - device->queryInterface(SLANG_UUID_IShaderCacheStatistics, (void**)shaderCacheStats.writeRef()); SLANG_CHECK(shaderCacheStats->getCacheMissCount() == 2); SLANG_CHECK(shaderCacheStats->getCacheHitCount() == 0); @@ -952,6 +976,10 @@ namespace gfx_test // This test does not modify shaders as other tests already test this, instead focusing on checking // that entries are correctly removed as cache limits are reached and that entries are always in // the right order. + // + // As opening multiple streams to the same file is dependent on the OS, this test is run on the + // in-memory file system. Cache eviction policy with an on-disk file system will need to be inspected + // manually. struct CacheWithMaxEntryLimit : MultipleEntryShaderCache { List<String> test0Lines; // C -> B -> A @@ -959,14 +987,12 @@ namespace gfx_test List<String> test2Lines; // A -> B List<String> test3Lines; // A -> C List<String> test4Lines; // C -> B -> A - List<String> entryKeys; // C, B, A - - ComPtr<IShaderCacheStatistics> shaderCacheStats; + List<String> entryKeys; // C, B, A void getCacheFile(List<String>& lines) { ComPtr<ISlangBlob> contentsBlob; - cacheFileSystem->loadFile(shaderCache.cacheFilename, contentsBlob.writeRef()); + memoryFileSystem->loadFile(shaderCache.cacheFilename, contentsBlob.writeRef()); List<UnownedStringSlice> temp; StringUtil::calcLines(UnownedStringSlice((char*)contentsBlob->getBufferPointer()), temp); for (auto line : temp) @@ -999,19 +1025,26 @@ namespace gfx_test shaderCache.entryCountLimit = 3; generateNewDevice(); createRequiredResources(); + modifyShaderA(contentsA); + modifyShaderB(contentsB); + modifyShaderC(contentsC); generateNewPipelineState(0); submitGPUWork(); generateNewPipelineState(1); submitGPUWork(); generateNewPipelineState(2); submitGPUWork(); - getCacheFile(test0Lines); device->queryInterface(SLANG_UUID_IShaderCacheStatistics, (void**)shaderCacheStats.writeRef()); SLANG_CHECK(shaderCacheStats->getCacheMissCount() == 3); SLANG_CHECK(shaderCacheStats->getCacheHitCount() == 0); SLANG_CHECK(shaderCacheStats->getCacheEntryDirtyCount() == 0); + // This needs to be called in order to force the cache file to be updated, otherwise we will + // be unable to perform the necessary checks. + freeOldResources(); + + getCacheFile(test0Lines); SLANG_CHECK(test0Lines.getCount() == 3); // This segment also doubles as the point where we fetch the keys for all three shaders @@ -1026,9 +1059,9 @@ namespace gfx_test } ComPtr<ISlangBlob> unused; - SLANG_CHECK(SLANG_SUCCEEDED(cacheFileSystem->loadFile(entryKeys[0].getBuffer(), unused.writeRef()))); - SLANG_CHECK(SLANG_SUCCEEDED(cacheFileSystem->loadFile(entryKeys[1].getBuffer(), unused.writeRef()))); - SLANG_CHECK(SLANG_SUCCEEDED(cacheFileSystem->loadFile(entryKeys[2].getBuffer(), unused.writeRef()))); + SLANG_CHECK(SLANG_SUCCEEDED(memoryFileSystem->loadFile(entryKeys[0].getBuffer(), unused.writeRef()))); + SLANG_CHECK(SLANG_SUCCEEDED(memoryFileSystem->loadFile(entryKeys[1].getBuffer(), unused.writeRef()))); + SLANG_CHECK(SLANG_SUCCEEDED(memoryFileSystem->loadFile(entryKeys[2].getBuffer(), unused.writeRef()))); } // Cache limit 2, access shaders A then B then C @@ -1046,19 +1079,21 @@ namespace gfx_test submitGPUWork(); generateNewPipelineState(2); submitGPUWork(); - getCacheFile(test1Lines); device->queryInterface(SLANG_UUID_IShaderCacheStatistics, (void**)shaderCacheStats.writeRef()); SLANG_CHECK(shaderCacheStats->getCacheMissCount() == 3); SLANG_CHECK(shaderCacheStats->getCacheHitCount() == 0); SLANG_CHECK(shaderCacheStats->getCacheEntryDirtyCount() == 0); + freeOldResources(); + + getCacheFile(test1Lines); SLANG_CHECK(test1Lines.getCount() == 2); ComPtr<ISlangBlob> unused; - SLANG_CHECK(SLANG_SUCCEEDED(cacheFileSystem->loadFile(entryKeys[0].getBuffer(), unused.writeRef()))); - SLANG_CHECK(SLANG_SUCCEEDED(cacheFileSystem->loadFile(entryKeys[1].getBuffer(), unused.writeRef()))); - SLANG_CHECK(SLANG_FAILED(cacheFileSystem->loadFile(entryKeys[2].getBuffer(), unused.writeRef()))); + SLANG_CHECK(SLANG_SUCCEEDED(memoryFileSystem->loadFile(entryKeys[0].getBuffer(), unused.writeRef()))); + SLANG_CHECK(SLANG_SUCCEEDED(memoryFileSystem->loadFile(entryKeys[1].getBuffer(), unused.writeRef()))); + SLANG_CHECK(SLANG_FAILED(memoryFileSystem->loadFile(entryKeys[2].getBuffer(), unused.writeRef()))); } // Cache limit 2, access shaders B and then A @@ -1070,19 +1105,21 @@ namespace gfx_test submitGPUWork(); generateNewPipelineState(0); submitGPUWork(); - getCacheFile(test2Lines); device->queryInterface(SLANG_UUID_IShaderCacheStatistics, (void**)shaderCacheStats.writeRef()); SLANG_CHECK(shaderCacheStats->getCacheMissCount() == 1); SLANG_CHECK(shaderCacheStats->getCacheHitCount() == 1); SLANG_CHECK(shaderCacheStats->getCacheEntryDirtyCount() == 0); + freeOldResources(); + + getCacheFile(test2Lines); SLANG_CHECK(test2Lines.getCount() == 2); ComPtr<ISlangBlob> unused; - SLANG_CHECK(SLANG_FAILED(cacheFileSystem->loadFile(entryKeys[0].getBuffer(), unused.writeRef()))); - SLANG_CHECK(SLANG_SUCCEEDED(cacheFileSystem->loadFile(entryKeys[1].getBuffer(), unused.writeRef()))); - SLANG_CHECK(SLANG_SUCCEEDED(cacheFileSystem->loadFile(entryKeys[2].getBuffer(), unused.writeRef()))); + SLANG_CHECK(SLANG_FAILED(memoryFileSystem->loadFile(entryKeys[0].getBuffer(), unused.writeRef()))); + SLANG_CHECK(SLANG_SUCCEEDED(memoryFileSystem->loadFile(entryKeys[1].getBuffer(), unused.writeRef()))); + SLANG_CHECK(SLANG_SUCCEEDED(memoryFileSystem->loadFile(entryKeys[2].getBuffer(), unused.writeRef()))); } // Cache limit 2, access shaders C and then A @@ -1094,19 +1131,21 @@ namespace gfx_test submitGPUWork(); generateNewPipelineState(0); submitGPUWork(); - getCacheFile(test3Lines); device->queryInterface(SLANG_UUID_IShaderCacheStatistics, (void**)shaderCacheStats.writeRef()); SLANG_CHECK(shaderCacheStats->getCacheMissCount() == 1); SLANG_CHECK(shaderCacheStats->getCacheHitCount() == 1); SLANG_CHECK(shaderCacheStats->getCacheEntryDirtyCount() == 0); + freeOldResources(); + + getCacheFile(test3Lines); SLANG_CHECK(test3Lines.getCount() == 2); ComPtr<ISlangBlob> unused; - SLANG_CHECK(SLANG_SUCCEEDED(cacheFileSystem->loadFile(entryKeys[0].getBuffer(), unused.writeRef()))); - SLANG_CHECK(SLANG_FAILED(cacheFileSystem->loadFile(entryKeys[1].getBuffer(), unused.writeRef()))); - SLANG_CHECK(SLANG_SUCCEEDED(cacheFileSystem->loadFile(entryKeys[2].getBuffer(), unused.writeRef()))); + SLANG_CHECK(SLANG_SUCCEEDED(memoryFileSystem->loadFile(entryKeys[0].getBuffer(), unused.writeRef()))); + SLANG_CHECK(SLANG_FAILED(memoryFileSystem->loadFile(entryKeys[1].getBuffer(), unused.writeRef()))); + SLANG_CHECK(SLANG_SUCCEEDED(memoryFileSystem->loadFile(entryKeys[2].getBuffer(), unused.writeRef()))); } // Cache limit 3, access shaders A then B then C @@ -1121,22 +1160,24 @@ namespace gfx_test submitGPUWork(); generateNewPipelineState(2); submitGPUWork(); - getCacheFile(test4Lines); device->queryInterface(SLANG_UUID_IShaderCacheStatistics, (void**)shaderCacheStats.writeRef()); SLANG_CHECK(shaderCacheStats->getCacheMissCount() == 1); SLANG_CHECK(shaderCacheStats->getCacheHitCount() == 2); SLANG_CHECK(shaderCacheStats->getCacheEntryDirtyCount() == 0); + freeOldResources(); + + getCacheFile(test4Lines); SLANG_CHECK(test4Lines.getCount() == 3); ComPtr<ISlangBlob> unused; - SLANG_CHECK(SLANG_SUCCEEDED(cacheFileSystem->loadFile(entryKeys[0].getBuffer(), unused.writeRef()))); - SLANG_CHECK(SLANG_SUCCEEDED(cacheFileSystem->loadFile(entryKeys[1].getBuffer(), unused.writeRef()))); - SLANG_CHECK(SLANG_SUCCEEDED(cacheFileSystem->loadFile(entryKeys[2].getBuffer(), unused.writeRef()))); + SLANG_CHECK(SLANG_SUCCEEDED(memoryFileSystem->loadFile(entryKeys[0].getBuffer(), unused.writeRef()))); + SLANG_CHECK(SLANG_SUCCEEDED(memoryFileSystem->loadFile(entryKeys[1].getBuffer(), unused.writeRef()))); + SLANG_CHECK(SLANG_SUCCEEDED(memoryFileSystem->loadFile(entryKeys[2].getBuffer(), unused.writeRef()))); } - void run() + void runTests() { runTest0(); runTest1(); @@ -1146,6 +1187,65 @@ namespace gfx_test checkCacheFiles(); } + + void run() + { + shaderCache.shaderCacheFileSystem = memoryFileSystem; + runTests(); + + cleanUpFiles(); + } + }; + + // This test is specifically for source files which live entirely in memory. The key difference between + // these and physical source files is such files have their contents hash added to the file dependencies + // list instead of a file path, meaning any given specific set of shader contents will be treated as a + // wholly unique module. + struct NonPhysicalFileDependencyEntry : BaseShaderCacheTest + { + void generateNewPipelineState(Slang::String shaderContents) + { + ComPtr<IShaderProgram> shaderProgram; + GFX_CHECK_CALL_ABORT(loadComputeProgramFromSource(device, shaderProgram, shaderContents)); + + ComputePipelineStateDesc pipelineDesc = {}; + pipelineDesc.program = shaderProgram.get(); + GFX_CHECK_CALL_ABORT( + device->createComputePipelineState(pipelineDesc, pipelineState.writeRef())); + } + + void runTests() + { + generateNewDevice(); + createRequiredResources(); + generateNewPipelineState(contentsA); + submitGPUWork(); + + device->queryInterface(SLANG_UUID_IShaderCacheStatistics, (void**)shaderCacheStats.writeRef()); + SLANG_CHECK(shaderCacheStats->getCacheMissCount() == 1); + SLANG_CHECK(shaderCacheStats->getCacheHitCount() == 0); + SLANG_CHECK(shaderCacheStats->getCacheEntryDirtyCount() == 0); + + generateNewDevice(); + createRequiredResources(); + generateNewPipelineState(contentsA); + submitGPUWork(); + + device->queryInterface(SLANG_UUID_IShaderCacheStatistics, (void**)shaderCacheStats.writeRef()); + SLANG_CHECK(shaderCacheStats->getCacheMissCount() == 0); + SLANG_CHECK(shaderCacheStats->getCacheHitCount() == 1); + SLANG_CHECK(shaderCacheStats->getCacheEntryDirtyCount() == 0); + + generateNewDevice(); + createRequiredResources(); + generateNewPipelineState(contentsC); + submitGPUWork(); + + device->queryInterface(SLANG_UUID_IShaderCacheStatistics, (void**)shaderCacheStats.writeRef()); + SLANG_CHECK(shaderCacheStats->getCacheMissCount() == 1); + SLANG_CHECK(shaderCacheStats->getCacheHitCount() == 0); + SLANG_CHECK(shaderCacheStats->getCacheEntryDirtyCount() == 0); + } }; template <typename T> @@ -1235,4 +1335,14 @@ namespace gfx_test { runTestImpl(shaderCacheTestImpl<SplitGraphicsShader>, unitTestContext, Slang::RenderApiFlag::Vulkan); } + + SLANG_UNIT_TEST(nonPhysicalFileDependenciesCacheEntryD3D12) + { + runTestImpl(shaderCacheTestImpl<NonPhysicalFileDependencyEntry>, unitTestContext, Slang::RenderApiFlag::D3D12); + } + + SLANG_UNIT_TEST(nonPhysicalFileDependenciesCacheEntryVulkan) + { + runTestImpl(shaderCacheTestImpl<NonPhysicalFileDependencyEntry>, unitTestContext, Slang::RenderApiFlag::Vulkan); + } } diff --git a/tools/gfx/persistent-shader-cache.cpp b/tools/gfx/persistent-shader-cache.cpp index c0fd3b44a..54c46f9dc 100644 --- a/tools/gfx/persistent-shader-cache.cpp +++ b/tools/gfx/persistent-shader-cache.cpp @@ -6,9 +6,15 @@ #include "../../source/core/slang-string-util.h" #include "../../source/core/slang-file-system.h" +#include "../../source/core/slang-char-util.h" + +#include <chrono> + namespace gfx { +using namespace std::chrono; + PersistentShaderCache::PersistentShaderCache(const IDevice::ShaderCacheDesc& inDesc) { desc = inDesc; @@ -35,52 +41,97 @@ PersistentShaderCache::PersistentShaderCache(const IDevice::ShaderCacheDesc& inD loadCacheFromFile(); } +PersistentShaderCache::~PersistentShaderCache() +{ + if (isMemoryFileSystem) + { + saveCacheToMemory(); + } +} + // Load a previous cache index saved to disk. If not found, create a new cache index // and save it to disk as filename. void PersistentShaderCache::loadCacheFromFile() { - ComPtr<ISlangBlob> indexBlob; - if (SLANG_FAILED(desc.shaderCacheFileSystem->loadFile(desc.cacheFilename, indexBlob.writeRef()))) + // We will need to combine the filename with the cache path in order to have the correct + // file path for initializing the stream. This needs to be done separately because there + // is no guarantee that the underlying file system is mutable. + String filePath; + if (mutableShaderCacheFileSystem) { - // Cache index not found, so we'll create and save a new one. - if (mutableShaderCacheFileSystem) + ComPtr<ISlangBlob> fullPath; + if (SLANG_FAILED(mutableShaderCacheFileSystem->getPath(PathKind::OperatingSystem, desc.cacheFilename, fullPath.writeRef()))) { - mutableShaderCacheFileSystem->saveFile(desc.cacheFilename, nullptr, 0); + // If we fail to obtain a physical file path, then this must be a MemoryFileSystem. In this case, file streams + // will not work as they require the file to be on disk, so we will rely on a fall back implementation. + isMemoryFileSystem = true; + loadCacheFromMemory(); return; } - // Cache index not found and we can't save a new one due to the file system being immutable. - return; + filePath = String((char*)fullPath->getBufferPointer()); + } + else + { + filePath = Path::combine(String(desc.shaderCachePath), String(desc.cacheFilename)); } - auto indexString = UnownedStringSlice((char*)indexBlob->getBufferPointer()); - - List<UnownedStringSlice> lines; - StringUtil::calcLines(indexString, lines); - for (auto line : lines) + if (SLANG_FAILED(indexStream.init(filePath, FileMode::Open, FileAccess::ReadWrite, FileShare::ReadWrite))) { - List<UnownedStringSlice> digests; - StringUtil::split(line, ' ', digests); // This will return our two hashes as two elements in digests, unless we've reached the end. - if (digests.getCount() != 2) - continue; - auto dependencyDigest = DigestUtil::fromString(digests[0]); - auto contentsDigest = DigestUtil::fromString(digests[1]); + // If we failed to open a stream to the file, then the file does not yet exist on disk. + // We will create the index file if our underlying file system is mutable. + if (mutableShaderCacheFileSystem) + { + indexStream.init(filePath, FileMode::Create, FileAccess::ReadWrite, FileShare::ReadWrite); + } + return; + } + else + { + const auto start = indexStream.getPosition(); + indexStream.seek(SeekOrigin::End, 0); + const auto end = indexStream.getPosition(); + indexStream.seek(SeekOrigin::Start, 0); + const Index numEntries = (Index)(end - start) / sizeof(ShaderCacheEntry); - ShaderCacheEntry entry = { dependencyDigest, contentsDigest }; - auto entryNode = entries.AddLast(entry); - keyToEntry.Add(dependencyDigest, entryNode); + if (desc.entryCountLimit > 0 && numEntries > desc.entryCountLimit) + { + // If the size limit for the current cache is smaller than the cache that produced the file we're trying to + // load, re-create the entire file. + // + // FileStream does not currently have any methods for truncating an existing file, so in this case, our cache + // index would no longer accurately reflect the state of our cache due to the extra now-garbage lines present. + // While this has no impact on cache operation, it could be problematic for debugging purposes, etc. + indexStream.close(); + indexStream.init(filePath, FileMode::Create, FileAccess::ReadWrite, FileShare::ReadWrite); + return; + } + else + { + // The cache index is not guaranteed to be ordered by most recent access, so we need a temporary list to store + // all the entries in order to sort them before filling in our linked list. + List<ShaderCacheEntry> tempEntries; + tempEntries.setCount(numEntries); + size_t bytesRead; + indexStream.read(tempEntries.getBuffer(), sizeof(ShaderCacheEntry) * numEntries, bytesRead); - // If there are more entries present in the cache file than the entry count limit allows, - // ignore the rest. Since we output the lines in the cache file in order of most to least - // recently used, the cache's behavior will still be correct. - if (desc.entryCountLimit > 0 && entries.Count() == desc.entryCountLimit) - break; + // We will need to sort tempEntries by last accessed time before we can add entries to our linked list. + tempEntries.quickSort(tempEntries.getBuffer(), 0, tempEntries.getCount() - 1, [](ShaderCacheEntry a, ShaderCacheEntry b) { return a.lastAccessedTime > b.lastAccessedTime; }); + for (auto& entry : tempEntries) + { + // If we reach this point, then the current cache is at least the same size in entries as the cache + // that produced the index we're reading in, so we don't need to check if we're exceeding capacity. + auto entryIndexNode = orderedEntries.AddLast(entries.getCount()); + entries.add(entry); + keyToEntry.Add(entry.dependencyBasedDigest, entryIndexNode); + } + } } } -LinkedNode<ShaderCacheEntry>* PersistentShaderCache::findEntry(const slang::Digest& key, ISlangBlob** outCompiledCode) +ShaderCacheEntry* PersistentShaderCache::findEntry(const slang::Digest& key, ISlangBlob** outCompiledCode) { - LinkedNode<ShaderCacheEntry>* entryNode; - if (!keyToEntry.TryGetValue(key, entryNode)) + LinkedNode<Index>* entryIndexNode; + if (!keyToEntry.TryGetValue(key, entryIndexNode)) { // The key was not found in the cache, so we return nullptr. *outCompiledCode = nullptr; @@ -90,19 +141,24 @@ LinkedNode<ShaderCacheEntry>* PersistentShaderCache::findEntry(const slang::Dige // If the key is found, load the stored contents from disk. We then move the corresponding // entry to the front of the linked list and update the cache file on disk desc.shaderCacheFileSystem->loadFile(DigestUtil::toString(key).getBuffer(), outCompiledCode); - if (entries.FirstNode() != entryNode) + auto index = entryIndexNode->Value; + entries[index].lastAccessedTime = (double)high_resolution_clock::now().time_since_epoch().count(); + if (orderedEntries.FirstNode() != entryIndexNode) { - entries.RemoveFromList(entryNode); - entries.AddFirst(entryNode); - if (mutableShaderCacheFileSystem) + orderedEntries.RemoveFromList(entryIndexNode); + orderedEntries.AddFirst(entryIndexNode); + if (mutableShaderCacheFileSystem && !isMemoryFileSystem) { - saveCacheToFile(); + auto offset = index * sizeof(ShaderCacheEntry); + indexStream.seek(SeekOrigin::Start, offset + 2 * sizeof(slang::Digest)); + indexStream.write(&entries[index].lastAccessedTime, sizeof(double)); + indexStream.flush(); } } - return entryNode; + return &entries[index]; } -void PersistentShaderCache::addEntry(const slang::Digest& dependencyDigest, const slang::Digest& astDigest, ISlangBlob* compiledCode) +void PersistentShaderCache::addEntry(const slang::Digest& dependencyDigest, const slang::Digest& contentsDigest, ISlangBlob* compiledCode) { if (!mutableShaderCacheFileSystem) { @@ -113,25 +169,45 @@ void PersistentShaderCache::addEntry(const slang::Digest& dependencyDigest, cons // Check that we do not exceed the cache's size limit by adding another entry. If so, // remove the least recently used entry first. // - // In theory, this could loop infinitely since deleteLRUEntry() immediately returns if - // mutableShaderCacheFileSystem is not set. However, this situation is functionally impossible - // because we check immediately before this as well. - while (desc.entryCountLimit > 0 && entries.Count() >= desc.entryCountLimit) + // In theory, the cache could be more than just one entry over the entry count limit. + // However, this is impossible in practice because we fully re-create the entry list + // and cache index file if the size of the current cache is smaller than the cache + // that generated the index file we loaded. In any case, the initial number of entries + // in the cache will always be fewer than the size limit and this check will be hit + // on the first entry added that exceeds the cache's size. + Index index = entries.getCount(); + if (desc.entryCountLimit > 0 && orderedEntries.Count() >= desc.entryCountLimit) { - deleteLRUEntry(); + index = deleteLRUEntry(); } - ShaderCacheEntry entry = { dependencyDigest, astDigest }; - auto entryNode = entries.AddFirst(entry); + auto lastAccessedTime = (double)high_resolution_clock::now().time_since_epoch().count(); + + ShaderCacheEntry entry = { dependencyDigest, contentsDigest, lastAccessedTime }; + auto entryNode = orderedEntries.AddFirst(index); + if (index == entries.getCount()) + { + // No entries were removed, so we can tack this entry on at the end. + entries.add(entry); + } + else + { + // An entry was deleted, so we overwrite that slot with the new entry. + entries[index] = entry; + } keyToEntry.Add(dependencyDigest, entryNode); mutableShaderCacheFileSystem->saveFileBlob(DigestUtil::toString(dependencyDigest).getBuffer(), compiledCode); - saveCacheToFile(); + if (!isMemoryFileSystem) + { + indexStream.seek(SeekOrigin::End, 0); + indexStream.write(&entry, sizeof(ShaderCacheEntry)); + indexStream.flush(); + } } void PersistentShaderCache::updateEntry( - LinkedNode<ShaderCacheEntry>* entryNode, const slang::Digest& dependencyDigest, const slang::Digest& contentsDigest, ISlangBlob* updatedCode) @@ -143,48 +219,99 @@ void PersistentShaderCache::updateEntry( return; } - entryNode->Value.contentsBasedDigest = contentsDigest; + // Unlike in addEntry(), we only update the contents digest here because the last accessed time will have already + // been updated while finding the entry. + auto entryIndexNode = *keyToEntry.TryGetValue(dependencyDigest); + auto index = entryIndexNode->Value; + entries[index].contentsBasedDigest = contentsDigest; mutableShaderCacheFileSystem->saveFileBlob(DigestUtil::toString(dependencyDigest).getBuffer(), updatedCode); - saveCacheToFile(); + if (!isMemoryFileSystem) + { + auto offset = index * sizeof(ShaderCacheEntry); + indexStream.seek(SeekOrigin::Start, offset + sizeof(slang::Digest)); + indexStream.write(&contentsDigest, sizeof(slang::Digest)); + indexStream.flush(); + } } -void PersistentShaderCache::saveCacheToFile() +Index PersistentShaderCache::deleteLRUEntry() { if (!mutableShaderCacheFileSystem) { - // Cannot save the index to disk if the underlying file system isn't mutable. - return; + // This is here as a safety precaution but should never be hit as + // addEntry() and its memory-based equivalent are the only functions + // that should call this. + return -1; } - StringBuilder indexSb; - for (auto& entry : entries) - { - indexSb << entry.dependencyBasedDigest; - indexSb << " "; - indexSb << entry.contentsBasedDigest; - indexSb << "\n"; - } + auto lruEntry = orderedEntries.LastNode(); + auto index = lruEntry->Value; + auto shaderKey = entries[index].dependencyBasedDigest; - mutableShaderCacheFileSystem->saveFile(desc.cacheFilename, indexSb.getBuffer(), indexSb.getLength()); + keyToEntry.Remove(shaderKey); + mutableShaderCacheFileSystem->remove(DigestUtil::toString(shaderKey).getBuffer()); + + orderedEntries.Delete(lruEntry); + return index; } -void PersistentShaderCache::deleteLRUEntry() +// An in-memory file system cannot utilize file streaming to update the index file in place. +// Consequently, the cache index file is updated once on exit and is guaranteed to maintain the +// correct order of entries from most to least recently used. However, any kind of interruption +// in program execution that results in the cache destructor not being called will result in an +// inaccurate cache index. +// +// These currently assume that the underlying file system must be a MemoryFileSystem as this is the +// only in-memory file system that currently exists in Slang, which is guaranteed to be mutable. +// Mutability checks will need to be added if this changes in the future. +void PersistentShaderCache::loadCacheFromMemory() { - if (!mutableShaderCacheFileSystem) + ComPtr<ISlangBlob> indexBlob; + if (SLANG_FAILED(mutableShaderCacheFileSystem->loadFile(desc.cacheFilename, indexBlob.writeRef()))) { - // This is here as a safety precaution but should never be hit as - // addEntry() is the only function that should call this. + mutableShaderCacheFileSystem->saveFile(desc.cacheFilename, nullptr, 0); return; } - auto lruEntry = entries.LastNode(); - auto shaderKey = lruEntry->Value.dependencyBasedDigest; + auto indexString = UnownedStringSlice((char*)indexBlob->getBufferPointer()); - keyToEntry.Remove(shaderKey); - mutableShaderCacheFileSystem->remove(DigestUtil::toString(shaderKey).getBuffer()); + List<UnownedStringSlice> lines; + StringUtil::calcLines(indexString, lines); + for (auto line : lines) + { + List<UnownedStringSlice> entryFields; + StringUtil::split(line, ' ', entryFields); + if (entryFields.getCount() != 2) + continue; + + ShaderCacheEntry entry; + entry.dependencyBasedDigest = DigestUtil::fromString(entryFields[0]); + entry.contentsBasedDigest = DigestUtil::fromString(entryFields[1]); + entry.lastAccessedTime = 0; + + auto entryNode = orderedEntries.AddLast(entries.getCount()); + entries.add(entry); + keyToEntry.Add(entry.dependencyBasedDigest, entryNode); + + if (desc.entryCountLimit > 0 && orderedEntries.Count() == desc.entryCountLimit) + break; + } +} - entries.Delete(lruEntry); +void PersistentShaderCache::saveCacheToMemory() +{ + StringBuilder indexSb; + for (auto& entryIndex : orderedEntries) + { + auto entry = entries[entryIndex]; + indexSb << entry.dependencyBasedDigest; + indexSb << " "; + indexSb << entry.contentsBasedDigest; + indexSb << "\n"; + } + + mutableShaderCacheFileSystem->saveFile(desc.cacheFilename, indexSb.getBuffer(), indexSb.getLength()); } } diff --git a/tools/gfx/persistent-shader-cache.h b/tools/gfx/persistent-shader-cache.h index 8d4ded4b9..8e05eddf6 100644 --- a/tools/gfx/persistent-shader-cache.h +++ b/tools/gfx/persistent-shader-cache.h @@ -7,27 +7,42 @@ #include "../../source/core/slang-string.h" #include "../../source/core/slang-dictionary.h" #include "../../source/core/slang-linked-list.h" +#include "../../source/core/slang-stream.h" namespace gfx { - using namespace Slang; +using namespace Slang; struct ShaderCacheEntry { slang::Digest dependencyBasedDigest; slang::Digest contentsBasedDigest; + double lastAccessedTime; + + bool operator==(const ShaderCacheEntry& rhs) + { + return dependencyBasedDigest == rhs.dependencyBasedDigest + && contentsBasedDigest == rhs.contentsBasedDigest + && lastAccessedTime == rhs.lastAccessedTime; + } + + uint32_t getHashCode() + { + return dependencyBasedDigest.getHashCode(); + } }; class PersistentShaderCache : public RefObject { public: PersistentShaderCache(const IDevice::ShaderCacheDesc& inDesc); + ~PersistentShaderCache(); // Fetch the cache entry corresponding to the provided key. If found, move the entry to // the front of entries and return the entry and the corresponding compiled code in // outCompiledCode. Else, return nullptr. - LinkedNode<ShaderCacheEntry>* findEntry(const slang::Digest& key, ISlangBlob** outCompiledCode); + ShaderCacheEntry* findEntry(const slang::Digest& key, ISlangBlob** outCompiledCode); // Add an entry to the cache with the provided key and contents hashes. If // adding an entry causes the cache to exceed size limitations, this will also @@ -36,38 +51,46 @@ public: // Update the contents hash for the specified entry in the cache and update the // corresponding file on disk. - void updateEntry( - LinkedNode<ShaderCacheEntry>* entryNode, - const slang::Digest& dependencyDigest, - const slang::Digest& contentsDigest, - ISlangBlob* updatedCode); + void updateEntry(const slang::Digest& dependencyDigest, const slang::Digest& contentsDigest, ISlangBlob* updatedCode); private: // Load a previous cache index saved to disk. If not found, create a new cache index // and save it to disk as filename. void loadCacheFromFile(); - // Update the cache index on disk. This should be called any time an entry changes. - void saveCacheToFile(); - // Delete the last entry (the least recently used) from entries, remove its key/value pair - // from keyToEntry, and remove the corresponding file on disk. This should only be called - // by addEntry() when the cache reaches maximum capacity. - void deleteLRUEntry(); + // from keyToEntry, and remove the corresponding file on disk. Returns the index in 'entries' + // of the removed entry so addEntry() can overwrite the corresponding entry in 'entries' + // with the new entry. This should only be called by addEntry() when the cache reaches maximum capacity. + Index deleteLRUEntry(); + + // Without access to a physical file path, in-memory file systems cannot leverage file streams and + // need to fall back on a different implementation for loading and saving the cache to memory. + void loadCacheFromMemory(); + void saveCacheToMemory(); // The shader cache's description. IDevice::ShaderCacheDesc desc; - // Dictionary mapping each shader's key to its corresponding node (entry) in the list - // of entries. - Dictionary<slang::Digest, LinkedNode<ShaderCacheEntry>*> keyToEntry; - - // Linked list containing the entries stored in the shader cache in order - // of most to least recently used. - LinkedList<ShaderCacheEntry> entries; - // The underlying file system used for the shader cache. ComPtr<ISlangMutableFileSystem> mutableShaderCacheFileSystem = nullptr; + bool isMemoryFileSystem = false; + + // A file stream to the index file opened during cache load. This will only + // exist for a cache that exists on-disk. + FileStream indexStream; + + // Dictionary mapping each shader's key to its corresponding node (entry) in the + // linked list 'orderedEntries'. + Dictionary<slang::Digest, LinkedNode<Index>*> keyToEntry; + + // Linked list containing the corresponding indices in 'entries' for entries in the + // shader cache ordered from most to least recently used. + LinkedList<Index> orderedEntries; + + // List of entries in the shader cache. This list is not guaranteed to be in order of recency + // as the main and fall back implementations handle outputting to the file differently. + List<ShaderCacheEntry> entries; }; } diff --git a/tools/gfx/renderer-shared.cpp b/tools/gfx/renderer-shared.cpp index 65c625d5d..3dcdb324c 100644 --- a/tools/gfx/renderer-shared.cpp +++ b/tools/gfx/renderer-shared.cpp @@ -331,19 +331,6 @@ void PipelineStateBase::initializeBase(const PipelineStateDesc& inDesc) } } -void updateCacheEntry(ISlangMutableFileSystem* fileSystem, slang::IBlob* compiledCode, String shaderFilename, slang::Digest ASTHash) -{ - auto hashSize = sizeof(slang::Digest); - - auto bufferSize = hashSize + compiledCode->getBufferSize(); - List<uint8_t> contents; - contents.setCount(bufferSize); - uint8_t* buffer = contents.begin(); - memcpy(buffer, &ASTHash, hashSize); - memcpy(buffer + hashSize, (void*)compiledCode->getBufferPointer(), compiledCode->getBufferSize()); - fileSystem->saveFile(shaderFilename.getBuffer(), buffer, bufferSize); -} - Result RendererBase::getEntryPointCodeFromShaderCache( slang::IComponentType* program, SlangInt entryPointIndex, @@ -374,7 +361,7 @@ Result RendererBase::getEntryPointCodeFromShaderCache( // Query the shader cache index for an entry with shaderKey as its key. auto entry = persistentShaderCache->findEntry(shaderKey, codeBlob.writeRef()); - if (entry && contentsHash == entry->Value.contentsBasedDigest) + if (entry && contentsHash == entry->contentsBasedDigest) { // We found the entry in the cache, and the entry's contents are up-to-date. Nothing else needs to be done. shaderCacheHitCount++; @@ -394,7 +381,7 @@ Result RendererBase::getEntryPointCodeFromShaderCache( } else { - persistentShaderCache->updateEntry(entry, shaderKey, contentsHash, codeBlob); + persistentShaderCache->updateEntry(shaderKey, contentsHash, codeBlob); shaderCacheEntryDirtyCount++; } } |
