summaryrefslogtreecommitdiffstats
path: root/tools/gfx
diff options
context:
space:
mode:
authorlucy96chen <47800040+lucy96chen@users.noreply.github.com>2022-10-19 09:36:41 -0700
committerGitHub <noreply@github.com>2022-10-19 09:36:41 -0700
commitdec930150f42a22892e567c769a1c9e24e761fde (patch)
tree92eea81e0c5d08c2c5ff090e7ebb0c2030648912 /tools/gfx
parent8add41a6e37994577d928bc312801ddfa1c33173 (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.slang20
-rw-r--r--tools/gfx/persistent-shader-cache.cpp24
-rw-r--r--tools/gfx/persistent-shader-cache.h20
-rw-r--r--tools/gfx/renderer-shared.cpp111
-rw-r--r--tools/gfx/renderer-shared.h7
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;