| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Name | Name | Last commit date | ||
|---|---|---|---|---|
parent directory.. | ||||
This tutorial expands the path tracing technique implemented in previous tutorial and demonstrates how to use the render state cache to save pipeline states created at run time and load them when the application starts.
Tutorial 25 shows how to package pipeline states off-line during the build process. This tutorial demonstrates an alternative approach, where pipeline states are created at run time, but when the application exits, they are saved to a file and loaded when the application starts next time. It also demonstrates a very useful feature of the render state cache: hot shader reload.
Render state cache object (IRenderStateCache) provides methods to create shaders and pipeline states that are identical to the methods of the render device. However, for each call the cache attempts to find the object data (e.g. compiled shader bytecode, pipeline state data etc.) to avoid expensive operation (such as shader compilation). If the data is not found, the object is created and its data is added to the cache. The cache data can be requested from the cache, stored in a file and loaded next time.
Another major feature that the state cache implements is hot shader reloading. The cache stores all data required to create shader objects, and when Reload method is called, the cache automatically detects which shaders need to be recompiled and which pipeline states need to be updated. The pipelines are updated transparently for the application.
To create a render state cache object, prepare an instance of the RenderStateCacheCreateInfo struct:
RenderStateCacheCreateInfo CacheCI;
CacheCI.pDevice = m_pDevice;
CacheCI.pArchiverFactory = LoadAndGetArchiverFactory();
// Enable hot state reload
CacheCI.EnableHotReload = true;
CreateRenderStateCache(CacheCI, &m_pStateCache);In this tutorial, we want to enable hot shader reloading, so we set the EnableHotReload member to true. Hot reloading intoduces some overhead and applications should generally disable this feature in production builds.
Render state cache also allows using separate shader source stream factory specifically for reloading, which can be specified through the pReloadSource member. In this example, we don't use this though.
At the next step, we check if the cache data from the previous application run exists and load it. We start by constructing the cache file path name. We use the FileSystem::GetLocalAppDataDirectory function that returns the platform-specific application data directory. If the directory does not exist, we create it:
m_StateCachePath = FileSystem::GetLocalAppDataDirectory("DiligentEngine-Tutorial26");
if (!FileSystem::PathExists(m_StateCachePath.c_str()))
{
// Create the directory if it does not exist
FileSystem::CreateDirectory(m_StateCachePath.c_str());
}Next, we append the file name to the path. We use different file names for each device type and configuration type. This is not required as a single cache file can hold all data, but splitting it into multiple files is more convenient.
m_StateCachePath.push_back(FileSystem::SlashSymbol);
m_StateCachePath += "state_cache_";
m_StateCachePath += GetRenderDeviceTypeShortString(m_pDevice->GetDeviceInfo().Type);
#ifdef DILIGENT_DEBUG
m_StateCachePath += "_d";
#else
m_StateCachePath += "_r";
#endif
m_StateCachePath += ".bin";Finally, we load the data into the cache, if it exists:
if (FileSystem::FileExists(m_StateCachePath.c_str()))
{
FileWrapper CacheDataFile{m_StateCachePath.c_str()};
RefCntAutoPtr<DataBlobImpl> pCacheData = DataBlobImpl::Create();
if (CacheDataFile->Read(pCacheData))
{
m_pStateCache->Load(pCacheData);
}
}Before the application exits, we request the data from the cache and store it to the file:
RefCntAutoPtr<IDataBlob> pCacheData;
m_pStateCache->WriteToBlob(&pCacheData);
FileWrapper CacheDataFile{m_StateCachePath.c_str(), EFileAccessMode::Overwrite};
CacheDataFile->Write(pCacheData->GetConstDataPtr(), pCacheData->GetSize());Second object that we need in this example is the render state notation parser, which loads the Diligent Render State Notation files:
RenderStateNotationParserCreateInfo ParserCI;
// Enable state reloading in the parser
ParserCI.EnableReload = true;
CreateRenderStateNotationParser(ParserCI, &m_pRSNParser);Note that we also need to enable hot shader reloading in the parser. Next, we load the DRSN file that describes pipeline states used in this tutorial:
m_pRSNParser->ParseFile("RenderStates.json", pShaderSourceFactory);Notice that in the previous tutorial we used the same file, but passed it over to the off-line packager tool. This time we load it at run time.
The state notation parser only loads the DRSN files, but does not create the actual objects. This task is performed by the render state notation loader that combines the render state cache and the state notation parser:
RenderStateNotationLoaderCreateInfo LoaderCI;
LoaderCI.pDevice = m_pDevice;
LoaderCI.pParser = m_pRSNParser;
LoaderCI.pStateCache = m_pStateCache;
LoaderCI.pStreamFactory = pShaderSourceFactory;
CreateRenderStateNotationLoader(LoaderCI, &m_pRSNLoader);Creating pipeline states is very similar to how it is done in the previous tutorial except that now we use the loader instead of the dearchiver. The loader uses a very similar API, e.g.:
LoadPipelineStateInfo LoadInfo;
LoadInfo.PipelineType = PIPELINE_TYPE_GRAPHICS;
LoadInfo.Name = "Resolve PSO";
// Define the callback to set the render target and depth stencil formats.
// These formats are only known at run time, so we can't define them in the
// render state notation file.
auto ModifyResolvePSODesc = MakeCallback(
[this](PipelineStateCreateInfo& PSODesc) {
// Similar to Tutorial 25
});
LoadInfo.ModifyPipeline = ModifyResolvePSODesc;
LoadInfo.pModifyPipelineData = ModifyResolvePSODesc;
m_pRSNLoader->LoadPipelineState(LoadInfo, &m_pResolvePSO);The loader transparently for the application uses the cache to create the state objects.
Shader create information can also be modified, e.g.:
ShaderMacroHelper Macros;
Macros.AddShaderMacro("BRDF_SAMPLING_MODE_COS_WEIGHTED", BRDF_SAMPLING_MODE_COS_WEIGHTED);
Macros.AddShaderMacro("BRDF_SAMPLING_MODE_IMPORTANCE_SAMPLING", BRDF_SAMPLING_MODE_IMPORTANCE_SAMPLING);
Macros.AddShaderMacro("BRDF_SAMPLING_MODE", m_BRDFSamplingMode);
// Add more macros
// ...
auto ModifyShaderCI = MakeCallback(
[&](ShaderCreateInfo& ShaderCI, SHADER_TYPE Type, bool& AddToLoaderCache) {
if (Type == SHADER_TYPE_PIXEL)
{
ShaderCI.Macros = Macros;
// Do not add the shader to the loader's cache as
// we may be recreating the shader at run-time.
AddToLoaderCache = false;
}
});
LoadPipelineStateInfo LoadInfo;
LoadInfo.ModifyShader = ModifyShaderCI;
LoadInfo.pModifyShaderData = ModifyShaderCI;
LoadInfo.PipelineType = PIPELINE_TYPE_GRAPHICS;
LoadInfo.Name = "Path Trace PSO";
// Do not use the loader's cache as we may be recreating the pipeline.
LoadInfo.AddToCache = false;
LoadInfo.LookupInCache = false;
m_pPathTracePSO.Release();
m_pRSNLoader->LoadPipelineState(LoadInfo, &m_pPathTracePSO);The loader has its own cache that holds objects previously created by the application and uses the object name as the key. In this example we recompile the path tracing pipeline at run time when some of the settings change. Since the pipelines use the same name, we don't want to use the cache, so we set LoadInfo.AddToCache = false and LoadInfo.LookupInCache = false. Note that the pipeline is always added to the render state cache, but unlike the loader, the cache can keep different pipeline states with the same name as it uses the full pipeline state description as the key.
Hot reloading as easy as calling
m_pStateCache->Reload();Try modifying shaders and pressing the Reload button in the UI - the effect will be immediate. All new shader variation will be added to the cache. An application may need to delete the cache if it contains a lot of intermediate states.
Note that there are some limitations to reloading functionality:
After pipeline states are loaded, they are used the same way as in the previous Tutorial.
Path tracing technique in this tutorial extends the method from Tutorial 25 and implements a number of major improvements:
Please refer to the shader source code for more details.
UI controls:
| Back | FazBrowse Home | New Git URL |