/* * @brief */ static r_program_t *R_LoadProgram(const char *name, void(*Init)(void)) { r_program_t *prog; char log[MAX_STRING_CHARS]; uint32_t e; int32_t i; for (i = 0; i < MAX_PROGRAMS; i++) { prog = &r_state.programs[i]; if (!prog->id) break; } if (i == MAX_PROGRAMS) { Com_Warn("MAX_PROGRAMS reached\n"); return NULL; } g_strlcpy(prog->name, name, sizeof(prog->name)); prog->id = qglCreateProgram(); prog->v = R_LoadShader(GL_VERTEX_SHADER, va("%s_vs.glsl", name)); prog->f = R_LoadShader(GL_FRAGMENT_SHADER, va("%s_fs.glsl", name)); if (prog->v) qglAttachShader(prog->id, prog->v->id); if (prog->f) qglAttachShader(prog->id, prog->f->id); qglLinkProgram(prog->id); qglGetProgramiv(prog->id, GL_LINK_STATUS, &e); if (!e) { qglGetProgramInfoLog(prog->id, sizeof(log) - 1, NULL, log); Com_Warn("%s: %s\n", prog->name, log); R_ShutdownProgram(prog); return NULL; } prog->Init = Init; if (prog->Init) { // invoke initialization function R_UseProgram(prog); prog->Init(); R_UseProgram(NULL); } R_GetError(prog->name); return prog; }
r_program_t* R_LoadProgram (const char* name, programInitFunc_t init, programUseFunc_t use) { r_program_t* prog; unsigned e; int i; /* shaders are deactivated */ if (!r_programs->integer) return nullptr; /* search existing one */ for (i = 0; i < MAX_PROGRAMS; i++) { prog = &r_state.programs[i]; if (Q_streq(prog->name, name)) return prog; } /* search free slot */ for (i = 0; i < MAX_PROGRAMS; i++) { prog = &r_state.programs[i]; if (!prog->id) break; } if (i == MAX_PROGRAMS) { Com_Printf("R_LoadProgram: MAX_PROGRAMS reached.\n"); return nullptr; } Q_strncpyz(prog->name, name, sizeof(prog->name)); prog->id = qglCreateProgram(); prog->v = R_LoadShader(GL_VERTEX_SHADER, va("%s_vs.glsl", name)); prog->f = R_LoadShader(GL_FRAGMENT_SHADER, va("%s_fs.glsl", name)); if (prog->v) qglAttachShader(prog->id, prog->v->id); if (prog->f) qglAttachShader(prog->id, prog->f->id); qglLinkProgram(prog->id); qglGetProgramiv(prog->id, GL_LINK_STATUS, &e); if (!e || !prog->v || !prog->f) { char log[MAX_STRING_CHARS]; qglGetProgramInfoLog(prog->id, sizeof(log) - 1, nullptr, log); Com_Printf("R_LoadProgram: %s: %s\n", prog->name, log); R_ShutdownProgram(prog); return nullptr; } prog->init = init; if (prog->init) { /* invoke initialization function */ R_UseProgram(prog); prog->init(prog); R_UseProgram(nullptr); } prog->use = use; Com_Printf("R_LoadProgram: '%s' loaded.\n", name); return prog; }
/* ================================================================================================ idRenderProgManager::LoadGLSLProgram ================================================================================================ */ void idRenderProgManager::LoadGLSLProgram( const int programIndex, const int vertexShaderIndex, const int fragmentShaderIndex ) { glslProgram_t & prog = glslPrograms[programIndex]; if ( prog.progId != INVALID_PROGID ) { return; // Already loaded } GLuint vertexProgID = ( vertexShaderIndex != -1 ) ? vertexShaders[ vertexShaderIndex ].progId : INVALID_PROGID; GLuint fragmentProgID = ( fragmentShaderIndex != -1 ) ? fragmentShaders[ fragmentShaderIndex ].progId : INVALID_PROGID; const GLuint program = qglCreateProgram(); if ( program ) { if ( vertexProgID != INVALID_PROGID ) { qglAttachShader( program, vertexProgID ); } if ( fragmentProgID != INVALID_PROGID ) { qglAttachShader( program, fragmentProgID ); } // bind vertex attribute locations for ( int i = 0; attribsPC[i].glsl != NULL; i++ ) { if ( ( attribsPC[i].flags & AT_VS_IN ) != 0 ) { qglBindAttribLocation( program, attribsPC[i].bind, attribsPC[i].glsl ); } } qglLinkProgram( program ); int infologLength = 0; qglGetProgramiv( program, GL_INFO_LOG_LENGTH, &infologLength ); if ( infologLength > 1 ) { char * infoLog = (char *)malloc( infologLength ); int charsWritten = 0; qglGetProgramInfoLog( program, infologLength, &charsWritten, infoLog ); // catch the strings the ATI and Intel drivers output on success if ( strstr( infoLog, "Vertex shader(s) linked, fragment shader(s) linked." ) != NULL || strstr( infoLog, "No errors." ) != NULL ) { //idLib::Printf( "render prog %s from %s linked\n", GetName(), GetFileName() ); } else { idLib::Printf( "While linking GLSL program %d with vertexShader %s and fragmentShader %s\n", programIndex, ( vertexShaderIndex >= 0 ) ? vertexShaders[vertexShaderIndex].name.c_str() : "<Invalid>", ( fragmentShaderIndex >= 0 ) ? fragmentShaders[ fragmentShaderIndex ].name.c_str() : "<Invalid>" ); idLib::Printf( "%s\n", infoLog ); } free( infoLog ); } } int linked = GL_FALSE; qglGetProgramiv( program, GL_LINK_STATUS, &linked ); if ( linked == GL_FALSE ) { qglDeleteProgram( program ); idLib::Error( "While linking GLSL program %d with vertexShader %s and fragmentShader %s\n", programIndex, ( vertexShaderIndex >= 0 ) ? vertexShaders[vertexShaderIndex].name.c_str() : "<Invalid>", ( fragmentShaderIndex >= 0 ) ? fragmentShaders[ fragmentShaderIndex ].name.c_str() : "<Invalid>" ); return; } if ( r_useUniformArrays.GetBool() ) { prog.vertexUniformArray = qglGetUniformLocation( program, VERTEX_UNIFORM_ARRAY_NAME ); prog.fragmentUniformArray = qglGetUniformLocation( program, FRAGMENT_UNIFORM_ARRAY_NAME ); assert( prog.vertexUniformArray != -1 || vertexShaderIndex < 0 || vertexShaders[vertexShaderIndex].uniforms.Num() == 0 ); assert( prog.fragmentUniformArray != -1 || fragmentShaderIndex < 0 || fragmentShaders[fragmentShaderIndex].uniforms.Num() == 0 ); } else { // store the uniform locations after we have linked the GLSL program prog.uniformLocations.Clear(); for ( int i = 0; i < RENDERPARM_TOTAL; i++ ) { const char * parmName = GetGLSLParmName( i ); GLint loc = qglGetUniformLocation( program, parmName ); if ( loc != -1 ) { glslUniformLocation_t uniformLocation; uniformLocation.parmIndex = i; uniformLocation.uniformIndex = loc; prog.uniformLocations.Append( uniformLocation ); } } // store the USER uniform locations for ( int i = 0; i < MAX_GLSL_USER_PARMS; i++ ) { const char * parmName = GetGLSLParmName( RENDERPARM_USER + i ); GLint loc = qglGetUniformLocation( program, parmName ); if ( loc != -1 ) { glslUniformLocation_t uniformLocation; uniformLocation.parmIndex = RENDERPARM_USER + i; uniformLocation.uniformIndex = loc; prog.uniformLocations.Append( uniformLocation ); } } // sort the uniforms based on index prog.uniformLocations.SortWithTemplate( idSort_QuickUniforms() ); } // get the uniform buffer binding for skinning joint matrices GLint blockIndex = qglGetUniformBlockIndex( program, "matrices_ubo" ); if ( blockIndex != -1 ) { qglUniformBlockBinding( program, blockIndex, 0 ); } // set the texture unit locations once for the render program. We only need to do this once since we only link the program once qglUseProgram( program ); for ( int i = 0; i < MAX_PROG_TEXTURE_PARMS; ++i ) { GLint loc = qglGetUniformLocation( program, va( "samp%d", i ) ); if ( loc != -1 ) { qglUniform1i( loc, i ); } } idStr programName = vertexShaders[ vertexShaderIndex ].name; programName.StripFileExtension(); prog.name = programName; prog.progId = program; prog.fragmentShaderIndex = fragmentShaderIndex; prog.vertexShaderIndex = vertexShaderIndex; }