I solved this now actually.
It is rather simple.
In my game, I press F5 to reload all files that has changed (and I see that they changed from their old modified date), including hlsl files (shaders).
When a shader file is detected to have changed, I simply remove it from the microcache and then reload it, which then recompiles the shader with its new code.
However, this is where it also crashes if it failed to compile, as detailed in this post.
But what I did now to fix it is to create a temporary copy of that shader and then try to compile that first, while also catching its exception with the exact error.
If it fails to compile, the real shader is untouched and will still have the latest working version, but if the temporary shader compilation succeeds, I can then recompile the real shader.
This approach successfully solves this whole problem, and you can easily output the error in any way you wish.
Here is the full code:
Code: Select all
uint32 _getHash(std::string source, std::string programName, uint32 seed = 0)
{
uint32 hash = FastHash(programName.c_str(), programName.length(), seed);
return FastHash(source.c_str(), source.length(), hash);
}
void GetShaderMicrocodeCache(GpuProgramPtr gpuProgramPtr, std::vector<uint32>& microcodes)
{
// Get the microcodes from the shader
if (CGeneric::m_renderSystem == CGeneric::eRenderSystem::Direct3D11)
{
uint32 seed = FastHash("D3D11", 5);
uint32 hash = _getHash(gpuProgramPtr->getSource(), gpuProgramPtr->getName(), seed);
microcodes.push_back(hash);
}
else
{
// Note that this most likely does not work for OpenGL. It only works for D3D9.
uint32 hash = _getHash(gpuProgramPtr->getSource(), gpuProgramPtr->getName());
microcodes.push_back(hash);
}
}
void RemoveShaderMicrocodeCache(GpuProgramPtr gpuProgramPtr)
{
// Check if microcode is being used
if (GpuProgramManager::getSingleton().getSaveMicrocodesToCache())
{
// Get the microcodes from the shader
std::vector<uint32> tmpMicrocodes;
GetShaderMicrocodeCache(gpuProgramPtr, tmpMicrocodes);
// Loop through all microcodes
for (size_t i = 0; i < tmpMicrocodes.size(); i++)
{
// Check if the microcode exists in the cache
uint32 tmpMicrocode = tmpMicrocodes[i];
if (GpuProgramManager::getSingleton().isMicrocodeAvailableInCache(tmpMicrocode))
// Remove the microcode from the cache
GpuProgramManager::getSingleton().removeMicrocodeFromCache(tmpMicrocode);
}
tmpMicrocodes.clear();
}
}
...
// Check if a GPU program exists for the shader
GpuProgramPtr tmpGpuProgramPtr = GpuProgramManager::getSingleton().getByName(fileName);
if (tmpGpuProgramPtr)
{
// Clone the shader
HighLevelGpuProgramPtr tmpClonedGpuProgramPtr = HighLevelGpuProgramManager::getSingleton().createProgram(
tmpGpuProgramPtr->getName() + "_TMP",
tmpGpuProgramPtr->getGroup(),
tmpGpuProgramPtr->getLanguage(),
tmpGpuProgramPtr->getType());
tmpClonedGpuProgramPtr->setSourceFile(tmpGpuProgramPtr->getSourceFile());
ParameterList tmpParameterList = tmpGpuProgramPtr->getParameters();
std::vector<std::string> tmpNames;
std::vector<std::string> tmpValues;
for (size_t i = 0; i < tmpParameterList.size(); i++)
{
const ParameterDef& tmpDef = tmpParameterList[i];
tmpNames.push_back(tmpDef.name);
tmpValues.push_back(tmpGpuProgramPtr->getParameter(tmpDef.name));
}
for (size_t i = 0; i < tmpNames.size(); i++)
tmpClonedGpuProgramPtr->setParameter(tmpNames[i], tmpValues[i]);
// Remove the microcache, otherwise it will not compile the new file version
RemoveShaderMicrocodeCache(tmpClonedGpuProgramPtr);
bool tmpSucceeded = true;
try
{
// Compile the temporary shader
tmpClonedGpuProgramPtr->load();
}
catch (Ogre::Exception &e)
{
// The compile failed
tmpSucceeded = false;
MessageBox(NULL, e.getDescription().c_str(), "Shader could not compile!", MB_OK | MB_ICONERROR | MB_TASKMODAL);
}
// Destroy the temporary shader
String tmpStr = tmpClonedGpuProgramPtr->getName();
HighLevelGpuProgramManager::getSingleton().unload(tmpStr);
HighLevelGpuProgramManager::getSingleton().remove(tmpStr);
tmpClonedGpuProgramPtr.reset();
if (MeshManager::getSingleton().resourceExists(tmpStr))
MessageBox(NULL, "The removal of the temporary shader resource failed, the resource is still in use somewhere else.", "", MB_OK);
// Check if the temporary shader could be compiled
if (tmpSucceeded)
{
// Remove the microcode from the cache
RemoveShaderMicrocodeCache(tmpGpuProgramPtr);
// Reload the program (which also compiles it from its new source file)
tmpGpuProgramPtr->reload();
// Output a message about the file reload
CString tmpSourceFile = tmpGpuProgramPtr->getSourceFile();
DebugOutput("Shader reloaded: " + tmpCurrentFile.fileName + " (" + tmpSourceFile + ")");
}
}