/* ================================================================================================ idRenderProgManager::LoadGLSLShader ================================================================================================ */ GLuint idRenderProgManager::LoadGLSLShader( GLenum target, const char * name, idList<int> & uniforms ) { idStr inFile; idStr outFileHLSL; idStr outFileGLSL; idStr outFileUniforms; inFile.Format( "renderprogs\\%s", name ); inFile.StripFileExtension(); outFileHLSL.Format( "renderprogs\\glsl\\%s", name ); outFileHLSL.StripFileExtension(); outFileGLSL.Format( "renderprogs\\glsl\\%s", name ); outFileGLSL.StripFileExtension(); outFileUniforms.Format( "renderprogs\\glsl\\%s", name ); outFileUniforms.StripFileExtension(); if ( target == GL_FRAGMENT_SHADER ) { inFile += ".pixel"; outFileHLSL += "_fragment.hlsl"; outFileGLSL += "_fragment.glsl"; outFileUniforms += "_fragment.uniforms"; } else { inFile += ".vertex"; outFileHLSL += "_vertex.hlsl"; outFileGLSL += "_vertex.glsl"; outFileUniforms += "_vertex.uniforms"; } // first check whether we already have a valid GLSL file and compare it to the hlsl timestamp; ID_TIME_T hlslTimeStamp; int hlslFileLength = fileSystem->ReadFile( inFile.c_str(), NULL, &hlslTimeStamp ); ID_TIME_T glslTimeStamp; int glslFileLength = fileSystem->ReadFile( outFileGLSL.c_str(), NULL, &glslTimeStamp ); // if the glsl file doesn't exist or we have a newer HLSL file we need to recreate the glsl file. idStr programGLSL; idStr programUniforms; if ( ( glslFileLength <= 0 ) || ( hlslTimeStamp > glslTimeStamp ) ) { if ( hlslFileLength <= 0 ) { // hlsl file doesn't even exist bail out return false; } void * hlslFileBuffer = NULL; int len = fileSystem->ReadFile( inFile.c_str(), &hlslFileBuffer ); if ( len <= 0 ) { return false; } idStr hlslCode( ( const char* ) hlslFileBuffer ); idStr programHLSL = StripDeadCode( hlslCode, inFile ); programGLSL = ConvertCG2GLSL( programHLSL, inFile, target == GL_VERTEX_SHADER, programUniforms ); fileSystem->WriteFile( outFileHLSL, programHLSL.c_str(), programHLSL.Length(), "fs_basepath" ); fileSystem->WriteFile( outFileGLSL, programGLSL.c_str(), programGLSL.Length(), "fs_basepath" ); if ( r_useUniformArrays.GetBool() ) { fileSystem->WriteFile( outFileUniforms, programUniforms.c_str(), programUniforms.Length(), "fs_basepath" ); } } else { // read in the glsl file void * fileBufferGLSL = NULL; int lengthGLSL = fileSystem->ReadFile( outFileGLSL.c_str(), &fileBufferGLSL ); if ( lengthGLSL <= 0 ) { idLib::Error( "GLSL file %s could not be loaded and may be corrupt", outFileGLSL.c_str() ); } programGLSL = ( const char * ) fileBufferGLSL; Mem_Free( fileBufferGLSL ); if ( r_useUniformArrays.GetBool() ) { // read in the uniform file void * fileBufferUniforms = NULL; int lengthUniforms = fileSystem->ReadFile( outFileUniforms.c_str(), &fileBufferUniforms ); if ( lengthUniforms <= 0 ) { idLib::Error( "uniform file %s could not be loaded and may be corrupt", outFileUniforms.c_str() ); } programUniforms = ( const char* ) fileBufferUniforms; Mem_Free( fileBufferUniforms ); } } // find the uniforms locations in either the vertex or fragment uniform array if ( r_useUniformArrays.GetBool() ) { uniforms.Clear(); idLexer src( programUniforms, programUniforms.Length(), "uniforms" ); idToken token; while ( src.ReadToken( &token ) ) { int index = -1; for ( int i = 0; i < RENDERPARM_TOTAL && index == -1; i++ ) { const char * parmName = GetGLSLParmName( i ); if ( token == parmName ) { index = i; } } for ( int i = 0; i < MAX_GLSL_USER_PARMS && index == -1; i++ ) { const char * parmName = GetGLSLParmName( RENDERPARM_USER + i ); if ( token == parmName ) { index = RENDERPARM_USER + i; } } if ( index == -1 ) { idLib::Error( "couldn't find uniform %s for %s", token.c_str(), outFileGLSL.c_str() ); } uniforms.Append( index ); } } // create and compile the shader const GLuint shader = qglCreateShader( target ); if ( shader ) { const char * source[1] = { programGLSL.c_str() }; qglShaderSource( shader, 1, source, NULL ); qglCompileShader( shader ); int infologLength = 0; qglGetShaderiv( shader, GL_INFO_LOG_LENGTH, &infologLength ); if ( infologLength > 1 ) { idTempArray<char> infoLog( infologLength ); int charsWritten = 0; qglGetShaderInfoLog( shader, infologLength, &charsWritten, infoLog.Ptr() ); // catch the strings the ATI and Intel drivers output on success if ( strstr( infoLog.Ptr(), "successfully compiled to run on hardware" ) != NULL || strstr( infoLog.Ptr(), "No errors." ) != NULL ) { //idLib::Printf( "%s program %s from %s compiled to run on hardware\n", typeName, GetName(), GetFileName() ); } else if ( r_displayGLSLCompilerMessages.GetBool() ) { idLib::Printf( "While compiling %s program %s\n", ( target == GL_FRAGMENT_SHADER ) ? "fragment" : "vertex" , inFile.c_str() ); const char separator = '\n'; idList<idStr> lines; lines.Clear(); idStr source( programGLSL ); lines.Append( source ); for ( int index = 0, ofs = lines[index].Find( separator ); ofs != -1; index++, ofs = lines[index].Find( separator ) ) { lines.Append( lines[index].c_str() + ofs + 1 ); lines[index].CapLength( ofs ); } idLib::Printf( "-----------------\n" ); for ( int i = 0; i < lines.Num(); i++ ) { idLib::Printf( "%3d: %s\n", i+1, lines[i].c_str() ); } idLib::Printf( "-----------------\n" ); idLib::Printf( "%s\n", infoLog.Ptr() ); } } GLint compiled = GL_FALSE; qglGetShaderiv( shader, GL_COMPILE_STATUS, &compiled ); if ( compiled == GL_FALSE ) { qglDeleteShader( shader ); return INVALID_PROGID; } } return shader; }
/** * @brief Reads/Preprocesses/Compiles the specified shader into a program. * @param[in] type The type of shader, currently either GL_VERTEX_SHADER or GL_FRAGMENT_SHADER. * @param[in] name The file name of the shader to load from ./base/shaders/ (e.g. "world_fs.glsl"). * @return A structure used as a handle to the compiled shader (program). */ static r_shader_t* R_LoadShader (const GLenum type, const char* name) { r_shader_t* sh; char path[MAX_QPATH], *src[1]; unsigned e, length[1]; char* srcBuf; byte* buf; int i; size_t bufLength = SHADER_BUF_SIZE; size_t initializeLength; #ifdef DEBUG /* Used to contain result of shader compile.*/ char log[MAX_STRING_CHARS]; #endif snprintf(path, sizeof(path), "shaders/%s", name); if (FS_LoadFile(path, &buf) == -1) { Com_DPrintf(DEBUG_RENDERER, "R_LoadShader: Failed to load ./base/shaders/%s.\n", name); return nullptr; } Com_DPrintf(DEBUG_RENDERER, "R_LoadShader: Loading ./base/shaders/%s.\n", name); char* const source = srcBuf = Mem_PoolAllocTypeN(char, bufLength, vid_imagePool); initializeLength = R_InitializeShader(type, name, srcBuf, bufLength); srcBuf += initializeLength; bufLength -= initializeLength; R_PreprocessShader(name, (const char*)buf, srcBuf, &bufLength); FS_FreeFile(buf); src[0] = source; length[0] = strlen(source); for (i = 0; i < MAX_SHADERS; i++) { sh = &r_state.shaders[i]; if (!sh->id) break; } if (i == MAX_SHADERS) { Com_Printf("R_LoadShader: MAX_SHADERS reached.\n"); Mem_Free(source); return nullptr; } Q_strncpyz(sh->name, name, sizeof(sh->name)); sh->type = type; sh->id = qglCreateShader(sh->type); if (!sh->id) { Mem_Free(source); return nullptr; } /* upload the shader source */ qglShaderSource(sh->id, 1, src, length); /* compile it and check for errors */ qglCompileShader(sh->id); Mem_Free(source); qglGetShaderiv(sh->id, GL_COMPILE_STATUS, &e); #ifdef DEBUG qglGetShaderInfoLog(sh->id, sizeof(log) - 1, nullptr, log); Com_Printf("R_LoadShader: %s: %s", sh->name, log); #endif if (!e) { #ifndef DEBUG char log[MAX_STRING_CHARS]; qglGetShaderInfoLog(sh->id, sizeof(log) - 1, nullptr, log); Com_Printf("R_LoadShader: %s: %s", sh->name, log); #endif qglDeleteShader(sh->id); OBJZERO(*sh); return nullptr; } return sh; }
/* * @brief */ static r_shader_t *R_LoadShader(GLenum type, const char *name) { r_shader_t *sh; char path[MAX_QPATH], *src[1], log[MAX_STRING_CHARS]; uint32_t e, length[1]; void *buf; int32_t i, len; g_snprintf(path, sizeof(path), "shaders/%s", name); if ((len = Fs_Load(path, &buf)) == -1) { Com_Warn("Failed to load %s\n", name); return NULL; } src[0] = (char *) buf; length[0] = len; for (i = 0; i < MAX_SHADERS; i++) { sh = &r_state.shaders[i]; if (!sh->id) break; } if (i == MAX_SHADERS) { Com_Warn("MAX_SHADERS reached\n"); Fs_Free(buf); return NULL; } g_strlcpy(sh->name, name, sizeof(sh->name)); sh->type = type; sh->id = qglCreateShader(sh->type); if (!sh->id) { Fs_Free(buf); return NULL; } // upload the shader source qglShaderSource(sh->id, 1, src, length); // compile it and check for errors qglCompileShader(sh->id); qglGetShaderiv(sh->id, GL_COMPILE_STATUS, &e); if (!e) { qglGetShaderInfoLog(sh->id, sizeof(log) - 1, NULL, log); Com_Warn("%s: %s\n", sh->name, log); qglDeleteShader(sh->id); memset(sh, 0, sizeof(*sh)); Fs_Free(buf); return NULL; } Fs_Free(buf); return sh; }