Issues:
1) CG include resolution (CgProgram::resolveIncludePaths) does not work recursively. If you have a zip file with something like "A/1.cg" include file "A/B/2.cg", which includes something like "C/3.cg", the compilation process says it can't open "C/3.cg". A more realistic scenario is something like "shaders/car.cg" including something like "details/car/high_end.cg", which requires something like "details/lighting/global_illumination.cg".
2) When including Cg files, the compiler reports line numbers that are erroneous due to included files bloating the number. (I haven't checked this recently, but I remember seeing it before).
Resolution:
CG added an include resolution callback in 2.1. I tested this and it appears to work fine in my CG code (it breaks with CGFX still, but I didn't mess with that yet).
Essentially, I removed the resolveIncludePaths and added a callback to let the CG virtual filesystem do the work...
Code: Select all
void CgProgram::loadFromSource(void)
{
...
// deal with includes
//String sourceToUse = resolveCgIncludes(mSource, this, mFilename);
String sourceToUse = mSource;
// Add the callback and indicate the context should use the current shader
// as the loading resource.
msResourceBeingLoaded = this;
cgSetCompilerIncludeCallback(mCgContext, &CgProgram::_resourceIncludeCallback);
// compile the file
mCgProgram = cgCreateProgram(mCgContext, CG_SOURCE, sourceToUse.c_str(),
mSelectedCgProfile, mEntryPoint.c_str(), const_cast<const char**>(mCgArguments));
// clear the virtual file system and remove the callback
cgSetCompilerIncludeString(mCgContext, NULL, NULL);
cgSetCompilerIncludeCallback(mCgContext, NULL);
msResourceBeingLoaded = NULL;
...
}
Code: Select all
void CgProgram::_resourceIncludeCallback(CGcontext context, const char *fileName)
{
String file = fileName;
// The CG runtime seems to prepend '/' to the virtual filesystem.
// This is bad for the ogre resource manager.
// Strip the / if it is there.
if(file.length() > 0 && file[0] == '/')
file = file.substr(1);
// This should never happen, being safe though.
if(msResourceBeingLoaded == NULL)
OGRE_EXCEPT(Exception::ERR_INTERNAL_ERROR, "Loading resource is null in cg include callback. This shouldn't happen.", __FILE__);
DataStreamPtr resource = ResourceGroupManager::getSingleton().
openResource(file, msResourceBeingLoaded->getGroup(), true, msResourceBeingLoaded);
// check if we found it.
// Note that CG will fallback on the file system if we failed, which will use compiler options like -I.
if(!resource.isNull()) {
cgSetCompilerIncludeString(context, fileName, resource->getAsString().c_str());
// detect an error if it occured.
// The only one should be a bad context, which is highly unlikely considering it's the same one.
CGerror error = cgGetError();
if(error != CG_NO_ERROR)
OGRE_EXCEPT(Exception::ERR_INTERNAL_ERROR, cgGetErrorString(error), __FILE__);
}
}
1) The use of a static variable is necessary (for the resource group and loading resource) and really should be context dependent, but I assume there's one context here and that one thread exists per context. Is that ok? I wish there was a use data type in the API. That variable scares me if multiple threads are loading resources, but I would think that isn't happening based on the context. So as long as cgCreateProgram is only executing on one thread and those lines around it are safe, I shouldn't have to add a mutex.
1b) Can I just pass the default resource group and not pass the loading resource? That would get rid of the static variable, but I don't know if there are side effects since I'm not as familiar with why the loading resource is there.
2) I'm clearing the VFS in Cg for each program. So the include resolution happens per loading high level program. The VFS could actually exist per resource group, but that seemed harder to manage since Cg manages the VFS per context. Is that a good assumption? Note that this way matches how the old resolution works.
3) The use of "/" for their filesystem is kind of a pain in the butt. You have to make sure to include files that are in zip archives as "/Path/In/Zip/file.cg". I don't think this breaks anything though, anyone have any insight? I used to include this stuff as "Path/In/Zip/file.cg", but that makes the current file path prepend to the include file. Using '/' stomps that prepend.
4) Is the CGFX resolution useful or required? I can actually test this since I have some CGFX files causing errors for this reason. I just don't need them for the final zip file.
Thanks!