Skip to content

Commit 5437663

Browse files
Render State Cache: add option to hash source files by name (close #706)
1 parent d0ed6ba commit 5437663

File tree

3 files changed

+130
-7
lines changed

3 files changed

+130
-7
lines changed

Graphics/GraphicsTools/interface/RenderStateCache.h

Lines changed: 41 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,29 @@ DILIGENT_TYPED_ENUM(RENDER_STATE_CACHE_LOG_LEVEL, Uint8)
4848
RENDER_STATE_CACHE_LOG_LEVEL_VERBOSE
4949
};
5050

51+
/// Hash mode used by the render state cache to identify unique files.
52+
DILIGENT_TYPED_ENUM(RENDER_STATE_CACHE_FILE_HASH_MODE, Uint8)
53+
{
54+
/// Hash files by their content.
55+
56+
/// This is the most reliable method, but it requires reading
57+
/// the entire file contents, as well as all included files,
58+
/// which may be time-consuming.
59+
RENDER_STATE_CACHE_FILE_HASH_MODE_BY_CONTENT,
60+
61+
/// Hash files by their names.
62+
63+
/// This method is very fast, but it it does not detect
64+
/// changes in the file contents.
65+
///
66+
/// This mode is not compatible with shader hot reloading.
67+
///
68+
/// \note If the file is modified after it has been cached,
69+
/// the cache will not detect the change and will continue
70+
/// to use the old cached version.
71+
RENDER_STATE_CACHE_FILE_HASH_MODE_BY_NAME
72+
};
73+
5174
// clang-format on
5275

5376
/// Render state cache create information.
@@ -64,8 +87,18 @@ struct RenderStateCacheCreateInfo
6487
/// Logging level, see Diligent::RENDER_STATE_CACHE_LOG_LEVEL.
6588
RENDER_STATE_CACHE_LOG_LEVEL LogLevel DEFAULT_INITIALIZER(RENDER_STATE_CACHE_LOG_LEVEL_NORMAL);
6689

90+
/// Source file hash mode, see Diligent::RENDER_STATE_CACHE_FILE_HASH_MODE.
91+
RENDER_STATE_CACHE_FILE_HASH_MODE FileHashMode DEFAULT_INITIALIZER(RENDER_STATE_CACHE_FILE_HASH_MODE_BY_CONTENT);
92+
6793
/// Whether to enable hot shader and pipeline state reloading.
6894

95+
/// When enabled, the cache will support the `Reload()` method
96+
/// that detects changes in the original shader source files
97+
/// and reloads the corresponding shaders and pipeline states.
98+
///
99+
/// Hot reloading requires that the file hash mode is
100+
/// `Diligent::RENDER_STATE_CACHE_FILE_HASH_MODE_BY_CONTENT`.
101+
///
69102
/// \note Hot reloading introduces some overhead and should
70103
/// generally be disabled in production builds.
71104
bool EnableHotReload DEFAULT_INITIALIZER(false);
@@ -86,15 +119,17 @@ struct RenderStateCacheCreateInfo
86119
{}
87120

88121
constexpr explicit RenderStateCacheCreateInfo(
89-
IRenderDevice* _pDevice,
90-
struct IArchiverFactory* _pArchiverFactory,
91-
RENDER_STATE_CACHE_LOG_LEVEL _LogLevel = RenderStateCacheCreateInfo{}.LogLevel,
92-
bool _EnableHotReload = RenderStateCacheCreateInfo{}.EnableHotReload,
93-
bool _OptimizeGLShaders = RenderStateCacheCreateInfo{}.OptimizeGLShaders,
94-
IShaderSourceInputStreamFactory* _pReloadSource = RenderStateCacheCreateInfo{}.pReloadSource) noexcept :
122+
IRenderDevice* _pDevice,
123+
struct IArchiverFactory* _pArchiverFactory,
124+
RENDER_STATE_CACHE_LOG_LEVEL _LogLevel = RenderStateCacheCreateInfo{}.LogLevel,
125+
RENDER_STATE_CACHE_FILE_HASH_MODE _FileHashMode = RenderStateCacheCreateInfo{}.FileHashMode,
126+
bool _EnableHotReload = RenderStateCacheCreateInfo{}.EnableHotReload,
127+
bool _OptimizeGLShaders = RenderStateCacheCreateInfo{}.OptimizeGLShaders,
128+
IShaderSourceInputStreamFactory* _pReloadSource = RenderStateCacheCreateInfo{}.pReloadSource) noexcept :
95129
pDevice{_pDevice},
96130
pArchiverFactory{_pArchiverFactory},
97131
LogLevel{_LogLevel},
132+
FileHashMode{_FileHashMode},
98133
EnableHotReload{_EnableHotReload},
99134
OptimizeGLShaders{_OptimizeGLShaders},
100135
pReloadSource{_pReloadSource}

Graphics/GraphicsTools/src/RenderStateCacheImpl.cpp

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,11 @@ RenderStateCacheImpl::RenderStateCacheImpl(IReferenceCounters* pRe
185185
LOG_ERROR_AND_THROW("CreateInfo.pArchiverFactory must not be null. Use LoadAndGetArchiverFactory() from ArchiverFactoryLoader.h to create the factory.");
186186
}
187187

188+
if (CreateInfo.FileHashMode == RENDER_STATE_CACHE_FILE_HASH_MODE_BY_NAME && CreateInfo.EnableHotReload)
189+
{
190+
LOG_WARNING_MESSAGE("Hot reloading is not compatible with by-name file hashing. Use by-content hashing instead.");
191+
}
192+
188193
SerializationDeviceCreateInfo SerializationDeviceCI;
189194
SerializationDeviceCI.DeviceInfo = m_pDevice->GetDeviceInfo();
190195
SerializationDeviceCI.AdapterInfo = m_pDevice->GetAdapterInfo();
@@ -315,6 +320,24 @@ bool RenderStateCacheImpl::CreateShader(const ShaderCreateInfo& ShaderCI,
315320
return FoundInCache;
316321
}
317322

323+
static void HashShaderCIByFileName(XXH128State& Hasher, const ShaderCreateInfo& ShaderCI)
324+
{
325+
ShaderCreateInfo HashCI = ShaderCI;
326+
HashCI.FilePath = nullptr;
327+
HashCI.Source = nullptr;
328+
Hasher.Update(HashCI);
329+
if (ShaderCI.FilePath != nullptr)
330+
{
331+
// Hash only the file path
332+
Hasher.UpdateStr(ShaderCI.FilePath);
333+
}
334+
else if (ShaderCI.Source != nullptr)
335+
{
336+
// Hash the source as raw string ignoring any include directives
337+
Hasher.UpdateStr(ShaderCI.Source, ShaderCI.SourceLength);
338+
}
339+
}
340+
318341
bool RenderStateCacheImpl::CreateShaderInternal(const ShaderCreateInfo& ShaderCI,
319342
IShader** ppShader)
320343
{
@@ -327,7 +350,19 @@ bool RenderStateCacheImpl::CreateShaderInternal(const ShaderCreateInfo& ShaderCI
327350
constexpr bool IsDebug = false;
328351
#endif
329352
ComputeDeviceAttribsHash(Hasher, m_pDevice);
330-
Hasher.Update(ShaderCI, IsDebug);
353+
if (m_CI.FileHashMode == RENDER_STATE_CACHE_FILE_HASH_MODE_BY_CONTENT)
354+
{
355+
Hasher.Update(ShaderCI);
356+
}
357+
else if (m_CI.FileHashMode == RENDER_STATE_CACHE_FILE_HASH_MODE_BY_NAME)
358+
{
359+
HashShaderCIByFileName(Hasher, ShaderCI);
360+
}
361+
else
362+
{
363+
UNEXPECTED("Unexpected file hash mode");
364+
}
365+
Hasher.Update(IsDebug);
331366
const XXH128Hash Hash = Hasher.Digest();
332367

333368
// First, try to check if the shader has already been requested

Tests/DiligentCoreAPITest/src/RenderStateCacheTest.cpp

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,7 @@ RefCntAutoPtr<IRenderStateCache> CreateCache(IRenderDevice* pD
253253
pDevice,
254254
GPUTestingEnvironment::GetInstance()->GetArchiverFactory(),
255255
RENDER_STATE_CACHE_LOG_LEVEL_VERBOSE,
256+
RENDER_STATE_CACHE_FILE_HASH_MODE_BY_CONTENT,
256257
HotReload,
257258
OptimizeGLShaders,
258259
pShaderReloadFactory,
@@ -843,6 +844,58 @@ TEST(RenderStateCacheTest, CreateComputePSO_Sign_Async)
843844
TestComputePSO(/*UseSignature = */ true, /*CompileAsync = */ true);
844845
}
845846

847+
848+
TEST(RenderStateCacheTest, CacheByFileName)
849+
{
850+
GPUTestingEnvironment* pEnv = GPUTestingEnvironment::GetInstance();
851+
IRenderDevice* pDevice = pEnv->GetDevice();
852+
853+
GPUTestingEnvironment::ScopedReset AutoReset;
854+
855+
RefCntAutoPtr<IShaderSourceInputStreamFactory> pShaderSourceFactory;
856+
pDevice->GetEngineFactory()->CreateDefaultShaderSourceStreamFactory("shaders/RenderStateCache", &pShaderSourceFactory);
857+
ASSERT_TRUE(pShaderSourceFactory);
858+
859+
RefCntAutoPtr<ITextureView> pTexSRV = CreateWhiteTexture();
860+
ASSERT_TRUE(pTexSRV);
861+
862+
RenderStateCacheCreateInfo CacheCI{
863+
pDevice,
864+
GPUTestingEnvironment::GetInstance()->GetArchiverFactory(),
865+
RENDER_STATE_CACHE_LOG_LEVEL_VERBOSE,
866+
RENDER_STATE_CACHE_FILE_HASH_MODE_BY_NAME,
867+
};
868+
869+
RefCntAutoPtr<IDataBlob> pData;
870+
for (Uint32 pass = 0; pass < 2; ++pass)
871+
{
872+
RefCntAutoPtr<IRenderStateCache> pCache;
873+
CreateRenderStateCache(CacheCI, &pCache);
874+
ASSERT_TRUE(pCache);
875+
876+
if (pData != nullptr)
877+
pCache->Load(pData, ContentVersion);
878+
879+
RefCntAutoPtr<IShader> pVS, pPS;
880+
CreateGraphicsShaders(pCache, pass == 0 ? pShaderSourceFactory : nullptr, SHADER_COMPILE_FLAG_NONE, pVS, pPS, /*PresentInCache = */ pData != nullptr);
881+
ASSERT_NE(pVS, nullptr);
882+
ASSERT_NE(pPS, nullptr);
883+
884+
VerifyGraphicsShaders(pVS, pPS, pTexSRV);
885+
886+
RefCntAutoPtr<IShader> pVS2, pPS2;
887+
CreateGraphicsShaders(pCache, nullptr, SHADER_COMPILE_FLAG_NONE, pVS2, pPS2, /*PresentInCache = */ true);
888+
EXPECT_EQ(pVS, pVS2);
889+
EXPECT_EQ(pPS, pPS);
890+
891+
if (pass == 0)
892+
{
893+
pCache->WriteToBlob(ContentVersion, &pData);
894+
ASSERT_NE(pData, nullptr);
895+
}
896+
}
897+
}
898+
846899
void CreateRayTracingShaders(IRenderStateCache* pCache,
847900
IShaderSourceInputStreamFactory* pShaderSourceFactory,
848901
RefCntAutoPtr<IShader>& pRayGen,

0 commit comments

Comments
 (0)