diff options
| author | lucy96chen <47800040+lucy96chen@users.noreply.github.com> | 2022-10-19 09:36:41 -0700 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2022-10-19 09:36:41 -0700 |
| commit | dec930150f42a22892e567c769a1c9e24e761fde (patch) | |
| tree | 92eea81e0c5d08c2c5ff090e7ebb0c2030648912 /tools/gfx | |
| parent | 8add41a6e37994577d928bc312801ddfa1c33173 (diff) | |
PersistentShaderCache integration (#2453)
* Shader cache index integrated into RendererBase; Added test for cache eviction policy (which currently does not pass)
* Restructured main if block in getEntryPointCodeFromShaderCache; Post-rebase cleanup
* undo local testing only change
* Fixed issues causing shader cache tests to fail
* Edited gfx.slang to reflect structural changes to IDevice::Desc and to include ShaderCacheDesc; Modified how the cache is reading in the file from disk; Added a check to the cache eviction policy test that checks for correct order of entries in the cache as well as eight total expected output files for D3D12 and Vulkan
* Removed line in gfx-unit-test.cpp for local testing
* Edited .gitignore to ignore all shaders automatically generated by the shader cache tests and removed the test shaders that were previously added; Review changes, most notably with an overhaul of how the cache eviction policy test handles checking order of entries
* Ran premake; Removed local testing specific line (again)
* Removed expected comparison files from earlier commit; Ran premake
* Edited premake5.lua to also ignore the auto-generated shader files from specific shader cache tests
* Fixed weird indent in premake5.lua
Diffstat (limited to 'tools/gfx')
| -rw-r--r-- | tools/gfx/gfx.slang | 20 | ||||
| -rw-r--r-- | tools/gfx/persistent-shader-cache.cpp | 24 | ||||
| -rw-r--r-- | tools/gfx/persistent-shader-cache.h | 20 | ||||
| -rw-r--r-- | tools/gfx/renderer-shared.cpp | 111 | ||||
| -rw-r--r-- | tools/gfx/renderer-shared.h | 7 |
5 files changed, 66 insertions, 116 deletions
diff --git a/tools/gfx/gfx.slang b/tools/gfx/gfx.slang index 13359334f..b4bd76470 100644 --- a/tools/gfx/gfx.slang +++ b/tools/gfx/gfx.slang @@ -1710,6 +1710,19 @@ struct SlangDesc slang::SlangLineDirectiveMode lineDirectiveMode = slang::SlangLineDirectiveMode::SLANG_LINE_DIRECTIVE_MODE_DEFAULT; }; +struct ShaderCacheDesc +{ + // The filename for the file the cache's state should be saved to or loaded from. + NativeString cacheFilename = "cache.txt"; + // The root directory for the shader cache. + NativeString shaderCachePath; + // The file system for loading cached shader kernels. The layer does not maintain a strong reference to the object, + // instead the user is responsible for holding the object alive during the lifetime of an `IDevice`. + void* shaderCacheFileSystem = nullptr; + // The maximum number of entries stored in the cache. + GfxCount entryCountLimit = 0; +}; + struct DeviceInteropHandles { InteropHandle handles[3] = {}; @@ -1733,11 +1746,8 @@ struct DeviceDesc void *apiCommandDispatcher = nullptr; // The slot (typically UAV) used to identify NVAPI intrinsics. If >=0 NVAPI is required. GfxIndex nvapiExtnSlot = -1; - // The root directory for the shader cache. - NativeString shaderCachePath; - // The file system for loading cached shader kernels. The layer does not maintain a strong reference to the object, - // instead the user is responsible for holding the object alive during the lifetime of an `IDevice`. - void *shaderCacheFileSystem = nullptr; + // Configurations for the shader cache. + ShaderCacheDesc shaderCache = {}; // Configurations for Slang compiler. SlangDesc slang = {}; diff --git a/tools/gfx/persistent-shader-cache.cpp b/tools/gfx/persistent-shader-cache.cpp index a378e0f4f..55a95a257 100644 --- a/tools/gfx/persistent-shader-cache.cpp +++ b/tools/gfx/persistent-shader-cache.cpp @@ -6,14 +6,15 @@ #include "../../source/core/slang-string-util.h" #include "../../source/core/slang-file-system.h" -namespace Slang +namespace gfx { -PersistentShaderCache::PersistentShaderCache(const Desc& inDesc) +PersistentShaderCache::PersistentShaderCache(const IDevice::ShaderCacheDesc& inDesc) { desc = inDesc; - if (!desc.shaderCachePath.getBuffer()) + // If a path is provided, we will want our underlying file system to be initialized using that path. + if (desc.shaderCachePath) { if (!desc.shaderCacheFileSystem) { @@ -25,7 +26,7 @@ PersistentShaderCache::PersistentShaderCache(const Desc& inDesc) } // If our shader cache has an underlying file system, check if it's mutable. If so, store a pointer - // to the mutable version in order to save new entries later. + // to the mutable version for operations which require writing to disk. if (desc.shaderCacheFileSystem) { desc.shaderCacheFileSystem->queryInterface(ISlangMutableFileSystem::getTypeGuid(), (void**)mutableShaderCacheFileSystem.writeRef()); @@ -39,27 +40,28 @@ PersistentShaderCache::PersistentShaderCache(const Desc& inDesc) void PersistentShaderCache::loadCacheFromFile() { ComPtr<ISlangBlob> indexBlob; - if (SLANG_FAILED(desc.shaderCacheFileSystem->loadFile(desc.cacheFilename.getBuffer(), indexBlob.writeRef()))) + if (SLANG_FAILED(desc.shaderCacheFileSystem->loadFile(desc.cacheFilename, indexBlob.writeRef()))) { // Cache index not found, so we'll create and save a new one. if (mutableShaderCacheFileSystem) { - mutableShaderCacheFileSystem->saveFile(desc.cacheFilename.getBuffer(), nullptr, 0); + mutableShaderCacheFileSystem->saveFile(desc.cacheFilename, nullptr, 0); return; } // Cache index not found and we can't save a new one due to the file system being immutable. return; } - String indexString; - File::readAllText(desc.cacheFilename, indexString); + auto indexString = UnownedStringSlice((char*)indexBlob->getBufferPointer()); List<UnownedStringSlice> lines; - StringUtil::calcLines(indexString.getUnownedSlice(), lines); + StringUtil::calcLines(indexString, lines); for (auto line : lines) { List<UnownedStringSlice> digests; - StringUtil::split(line, ' ', digests); // This will return our two hashes as two elements in 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 astDigest = DigestUtil::fromString(digests[1]); @@ -158,7 +160,7 @@ void PersistentShaderCache::saveCacheToFile() indexSb << "\n"; } - mutableShaderCacheFileSystem->saveFile(desc.cacheFilename.getBuffer(), indexSb.getBuffer(), indexSb.getLength()); + mutableShaderCacheFileSystem->saveFile(desc.cacheFilename, indexSb.getBuffer(), indexSb.getLength()); } void PersistentShaderCache::deleteLRUEntry() diff --git a/tools/gfx/persistent-shader-cache.h b/tools/gfx/persistent-shader-cache.h index 55738a9af..49a4a53dd 100644 --- a/tools/gfx/persistent-shader-cache.h +++ b/tools/gfx/persistent-shader-cache.h @@ -1,34 +1,28 @@ // slang-shader-cache-index.h #pragma once #include "../../slang.h" +#include "../../slang-gfx.h" #include "../../slang-com-ptr.h" #include "../../source/core/slang-string.h" #include "../../source/core/slang-dictionary.h" #include "../../source/core/slang-linked-list.h" -namespace Slang +namespace gfx { + using namespace Slang; + struct ShaderCacheEntry { slang::Digest dependencyBasedDigest; slang::Digest astBasedDigest; }; -class PersistentShaderCache +class PersistentShaderCache : public RefObject { public: - // TODO: Remove in integration PR in favor of new ShaderCacheDesc in slang-gfx.h - struct Desc - { - String cacheFilename; - String shaderCachePath; - SlangInt entryCountLimit = 1000; - ISlangFileSystem* shaderCacheFileSystem = nullptr; - }; - - PersistentShaderCache(const Desc& inDesc); + PersistentShaderCache(const IDevice::ShaderCacheDesc& inDesc); // 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 @@ -62,7 +56,7 @@ private: void deleteLRUEntry(); // The shader cache's description. - Desc desc; + IDevice::ShaderCacheDesc desc; // Dictionary mapping each shader's key to its corresponding node (entry) in the list // of entries. diff --git a/tools/gfx/renderer-shared.cpp b/tools/gfx/renderer-shared.cpp index 48cd6907f..c7d7933a3 100644 --- a/tools/gfx/renderer-shared.cpp +++ b/tools/gfx/renderer-shared.cpp @@ -351,11 +351,8 @@ Result RendererBase::getEntryPointCodeFromShaderCache( slang::IBlob** outCode, slang::IBlob** outDiagnostics) { - // TODO: Need a way in filesystem to query both file size and file creation time, if cache size exceeds - // specified maximum size (in bytes or files) then delete oldest files - cache eviction policy - - // Immediately call getEntryPointCode if no shader cache was provided on initialization - if (!shaderCacheFileSystem) + // Immediately call getEntryPointCode if no shader cache has been initialized + if (!persistentShaderCache) { return program->getEntryPointCode(entryPointIndex, targetIndex, outCode, outDiagnostics); } @@ -365,75 +362,40 @@ Result RendererBase::getEntryPointCodeFromShaderCache( ComPtr<slang::ISession> session; getSlangSession(session.writeRef()); - slang::Digest shaderKeyHash; - program->computeDependencyBasedHash(entryPointIndex, targetIndex, &shaderKeyHash); - - String shaderKey = DigestUtil::toString(shaderKeyHash); + slang::Digest shaderKey; + program->computeDependencyBasedHash(entryPointIndex, targetIndex, &shaderKey); // Produce a hash using the AST for this program - This is needed to check whether a cache entry is effectively dirty, // or to save along with the compiled code into an entry so the entry can be checked if fetched later on. - slang::Digest ASTHash; - program->computeASTBasedHash(&ASTHash); - + slang::Digest contentsHash; + program->computeASTBasedHash(&contentsHash); + ComPtr<ISlangBlob> codeBlob; - // Query shaderCacheFileSystem for an entry whose key matches shaderFilename - // - If we find it, then copy the file contents into memory and return in outCode - auto result = shaderCacheFileSystem->loadFile(shaderKey.getBuffer(), codeBlob.writeRef()); - - if (SLANG_FAILED(result)) + // 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.astBasedDigest) { - // If we didn't find it, call program->getEntryPointCode() to get and return the code. We also - // make sure to save a new entry in the shader cache. - if (SLANG_SUCCEEDED(program->getEntryPointCode(entryPointIndex, targetIndex, codeBlob.writeRef(), outDiagnostics))) - { - if (mutableShaderCacheFileSystem) - { - updateCacheEntry(mutableShaderCacheFileSystem, codeBlob, shaderKey, ASTHash); - } - - shaderCacheMissCount++; - } - else - { - // If getEntryPointCode() failed to fetch the code, we return SLANG_FAIL along with the diagnostics output - // in outDiagnostics. - return SLANG_FAIL; - } + // We found the entry in the cache, and the entry's contents are up-to-date. Nothing else needs to be done. + shaderCacheHitCount++; } else { - // If the entry exists, we need to check that the entry isn't effectively dirty. Since we stored - // the AST hash with the compiled code, we can determine this by comparing the stored hash with the - // AST hash generated earlier. - auto entryContents = codeBlob->getBufferPointer(); - auto hashSize = sizeof(slang::Digest); - if (memcmp(ASTHash.values, entryContents, hashSize) != 0) - { - // The AST hash stored in the entry does not match the AST hash generated earlier, indicating - // that the shader code has changed and the entry needs to be updated. - if (SLANG_SUCCEEDED(program->getEntryPointCode(entryPointIndex, targetIndex, codeBlob.writeRef(), outDiagnostics))) - { - if (mutableShaderCacheFileSystem) - { - updateCacheEntry(mutableShaderCacheFileSystem, codeBlob, shaderKey, ASTHash); - } + // There are two possibilities: the entry does not exist in the cache, or the entry's contents are out-of-date. + // Both will require calling getEntryPointCode() in order to fetch the correct compiled code, so we'll do that now. + SLANG_RETURN_ON_FAIL(program->getEntryPointCode(entryPointIndex, targetIndex, codeBlob.writeRef(), outDiagnostics)); - shaderCacheEntryDirtyCount++; - } - else - { - // If getEntryPointCode() failed to fetch the code, we return SLANG_FAIL along with the diagnostics output - // in outDiagnostics. - return SLANG_FAIL; - } + // If the entry was not found in the cache, let's add it. Otherwise, the entry's contents were out-of-date, so let's + // update the entry with the updated contents. + if (!entry) + { + persistentShaderCache->addEntry(shaderKey, contentsHash, codeBlob); + shaderCacheMissCount++; } else { - auto compiledCode = RawBlob::create((uint8_t*)codeBlob->getBufferPointer() + hashSize, codeBlob->getBufferSize() - hashSize); - codeBlob = compiledCode; - - shaderCacheHitCount++; + persistentShaderCache->updateEntry(entry, shaderKey, contentsHash, codeBlob); + shaderCacheEntryDirtyCount++; } } @@ -463,29 +425,12 @@ IDevice* gfx::RendererBase::getInterface(const Guid& guid) SLANG_NO_THROW Result SLANG_MCALL RendererBase::initialize(const Desc& desc) { - // TODO: This logic for initalizing the shader cache has been replicated inside - // the constructor for ShaderCacheIndex. Remove when ShaderCacheIndex is integrated in. - - // If a shader cache file system was provided, use the provided system. - if (desc.shaderCacheFileSystem) - { - shaderCacheFileSystem = desc.shaderCacheFileSystem; - } - if (desc.shaderCachePath) - { - // Only a path was provided, create a RelativeFileSystem using the path - if (!shaderCacheFileSystem) - { - shaderCacheFileSystem = OSFileSystem::getMutableSingleton(); - } - shaderCacheFileSystem = new RelativeFileSystem(shaderCacheFileSystem, desc.shaderCachePath); - } - - // If we initialized a file system for the shader cache, check if it's mutable. If so, store a pointer - // to the mutable version in order to save new entries later. - if (shaderCacheFileSystem) + auto cacheDesc = desc.shaderCache; + // We only want to initialize the shader cache if either a shader cache path or file system + // was provided. + if (cacheDesc.shaderCachePath || cacheDesc.shaderCacheFileSystem) { - shaderCacheFileSystem->queryInterface(ISlangMutableFileSystem::getTypeGuid(), (void**)mutableShaderCacheFileSystem.writeRef()); + persistentShaderCache = new PersistentShaderCache(desc.shaderCache); } if (desc.apiCommandDispatcher) diff --git a/tools/gfx/renderer-shared.h b/tools/gfx/renderer-shared.h index fc1c35d20..01111e292 100644 --- a/tools/gfx/renderer-shared.h +++ b/tools/gfx/renderer-shared.h @@ -5,6 +5,8 @@ #include "core/slang-basic.h" #include "core/slang-com-object.h" +#include "persistent-shader-cache.h" + #include "resource-desc-utils.h" namespace gfx @@ -1372,10 +1374,7 @@ public: SlangContext slangContext; ShaderCache shaderCache; - // TODO: These should be removed when ShaderCacheIndex is ready to be integrated. ShaderCacheIndex - // will be responsible for keeping track of the underlying filesystem for the cache. - ISlangFileSystem* shaderCacheFileSystem = nullptr; - ComPtr<ISlangMutableFileSystem> mutableShaderCacheFileSystem = nullptr; + RefPtr<PersistentShaderCache> persistentShaderCache = nullptr; Slang::Dictionary<slang::TypeLayoutReflection*, Slang::RefPtr<ShaderObjectLayoutBase>> m_shaderObjectLayoutCache; Slang::ComPtr<IPipelineCreationAPIDispatcher> m_pipelineCreationAPIDispatcher; |
